220 likes | 339 Views
標準 Java 仮想機械上で 動的 にメンバーの追加 を行う機構の提案. 東京工業大学 早船 総一郎 千葉 滋. 動的な振る舞いの変更. 既に実行しているプログラムの振る舞いを変更 サーバを止めずに セキュリティパッチを適用 性能を調査 対話的開発. 切り替え. 新しいコード. 古いコード. サーバー. 典型的な利用シナリオ. オフラインで型検査等を実行 コンパイル済みのクラス定義を準備 対話的に新しいクラス定義を送り込む RMI や JDWP を利用. 古いコード. ユーザ ホスト. サーバー. 新しいコード.
E N D
標準 Java 仮想機械上で動的にメンバーの追加を行う機構の提案 東京工業大学 早船 総一郎 千葉 滋
動的な振る舞いの変更 • 既に実行しているプログラムの振る舞いを変更 • サーバを止めずに • セキュリティパッチを適用 • 性能を調査 • 対話的開発 切り替え 新しいコード 古いコード サーバー
典型的な利用シナリオ • オフラインで型検査等を実行 • コンパイル済みのクラス定義を準備 • 対話的に新しいクラス定義を送り込む • RMI や JDWP を利用 古いコード ユーザホスト サーバー 新しいコード
動的なクラス定義の変更 • メソッドの中身の変更 • シグネチャは変わらない • メンバーの追加 • 仮想関数テーブルが更新 class Position { void move(int i){ x = x + i; } } class Main{ void action (Position p) { p.move(1); }} 追加 class Position { void move(inti){ x = x + i; } int distance(inti,int j){ return sqrt((x – i)^2 + (y – j)^2); } } 変更 class Main { void action (Position p) { p.distance(0,0); }}
HotSwap による動的な振る舞いの変更 • 標準 Java 仮想機械で提供 • メソッドボディの内容を異なるものに変更 • メンバーの追加は不可 • 最適化のため? class Position { void move(int i){ x = x + i; } } class Main{ void action (Position p) { p.move(1); }} class Position { void move(inti){ x = x + i; } int distance(inti,int j){ return sqrt((x – i)^2 + (y – j)^2); } } 変更 class Main { void action (Position p) { p.distance(0,0); }}
提案:動的なメンバーの追加を可能に • 標準 Java 仮想機械を利用 • JavaAgent, Javassistを利用 • プログラムの監視、バイトコードの変換 • RMI による対話的な更新 本システム Javassist RMI Java Agent ユーザホスト 新しいコード 古いコード
実現:ロード時に汎用性の高いメンバーを準備実現:ロード時に汎用性の高いメンバーを準備 class Main{ void action (Position p) { p.move(1); }} class Position { void move(int i){x = x + i;} } class Position { void move(inti){x = x + i;} } 追加 変換 blank$ (…){ 空 } class Position { void move(inti){x = x + i;} int distance(inti,int j){ return sqrt((x – i)^2 + (y – j)^2); } } class Main { void action (Position p) { p.distance(0,0); }} class Main{ void action(Position p) { }} class Position { void move(int i){x = x + i;} } blank$ の呼び出し blank$ (…){/* distance の処理 */}
メンバーを追加してからロード • ロード時なら標準 Java 仮想機械でも可能 • 汎用性の高いメソッド • 返り値の型、引数の型は Object と Object の配列に • 重複しないようにメソッド名を準備 class Position { void move(int i){x = x + i;} } ロード時に 追加 class Position { void move(inti){x = x + i;} } public Object blank$ (Object[] objects) { return null; }
実行中にメソッドを追加 • distance メソッドを追加 • action メソッドが distance を呼ぶ class Position { void move(inti){x = x + i;} int distance(inti,int j){ return sqrt((x – i)^2 + (y – j)^2); } } class Main { void action (Position p) { p.distance(0,0); }} class Main{ void action(Position p) { }} class Position { void move(int i){x = x + i;} } blank$ の呼び出し blank$ (…){/* distance の処理 */}
追加された側のクラスの変換 • 追加するメソッドの中身を準備しておいたメソッドにコピー • 実行時に型変換するコードを挿入 class Position { void move(inti){x = x + i;} int distance(inti,int j){ return sqrt((x – i)^2 + (y – j)^2); } } public Object blank$(Object[] objects) { } 型変換するコード int → Object Object[] → int, int return sqrt((x – i)^2 + (y – j)^2); class Position { void move(int i){x = x + i;} } blank$ (…){/* distance の処理 */}
呼び出し側の変換 • 追加したメソッドは実際には存在しない • blank$ を呼び出すように変換 class Main { void action (Position p) { p.distance(0,0); }} p.blank$(new Object[] { 0, 0}); class Main{ void action(Position p) { }} class Position { void move(int i){x = x + i;} } blank$ の呼び出し blank$ (…){/* distance の処理 */}
オーバーライドの考慮 class Data { void move(int i){x = x +i;} int distance(int i,int j){ return ((x – i)^2 + (y – j)^2)^(1/2); }} • メソッドにはオーバライドがある • 呼び出される対象が変化 class Position { void move(int i){x = x +i;} } main (Position p) { p.distance(0, 0); } 呼出? class Rect { void move(int i){x = x +i;} int distance(int i,int j){ return ((x – i)^2 + (y – j)^2)^(1/2); }} class Circle{ void move(int i){x = x +i;} }
汎用性の高いメソッドを準備 • オーバーライドされているメソッドを呼び出しておく class Circle{ void move(int i){x = x +i;} } class Position { void move(int i){x = x +i;} } 呼出 public Object blank$ (Object[] objects) { return super.blank$(objects); } blank$ メソッド
追加の実現 class Position { void move(int i){x = x +i;} } • 各クラスにメソッドを準備 • 意図しないオーバーライド • blank$ メソッドの呼び出し • オブジェクトの動的な型 • Circle のblank$ が呼び出される class Circle{ void move(int i){x = x +i;} } 呼出 public Object blank$ (Object[] objects) { } blank$ メソッド main (Position p) { } p.blank$(new Object[] { 0,0 }); 型変換するコード 呼出 distance の処理
呼び出し側の変換 class Position { void move(int i){x = x +i;} } • オーバーライドに対する制御 • 型の確認 • 呼び出すメソッドの切り替え main (Position p) { } class Rect{ void move(int i){o = o +i;} int distance(int i,int j){ return ((x – i)^2 + (y – j)^2)^(1/2); } } if(p instanceof Rect){ p.distance(0,0); } else if(p instanceof Position){ p.blank$(new Object[] { 0,0 }); } else { p.distance(0,0); } 呼出 public Object blank$ (Object[] objects) { } 型変換するコード blank$ メソッド distance の処理
フィールド、コンストラクタの追加 • メソッドと似たような処理 class Position { void move(int i){x = x +i;} } 準備 class Position { void move(int i){x = x +i;} } main (Position p) { p.move(1); } blank$ フィールド blank$ コンストラクタ 変更 追加 class Position { void move(int i){x = x +i;} } main (Position p) { } blank$ のアクセス blank$ の呼び出し blank$ フィールド blank$ コンストラクタ
ユーザーが追加や変更を指示する方法 • 対話的に新しいクラス定義の変更を送る • メンバーに関しては追加のみを許し、削除を許していない • 追加が行えるタイミングとしては標準の Hotswap と同様である
実験 • マイクロベンチマークを作成する • 空のメンバーの準備 • いくつかの異なるクラスをロードする時間を計測 • オーバーライドに対する制御 • 本システムで追加をしたメソッドを呼び出す時間を計測 • 実験環境 • OS: Windows7 • CPU: Intel Core2Quad 2.67GHz • メモリ: 4GB
実験:空のメンバーの準備 • クラス一つあたりのロード時間 • 本システム不使用:約0.26ms、本システム利用:1.33ms • 4~5倍のオーバーヘッド
実験:オーバーライドに対する制御 • 一回のメソッド呼び出し • 型の確認なし:5.262ns、型の確認あり:10.04ns • 2倍のオーバーヘッド
関連研究 • Dynamic Virtual Machine [‘00 Malabarba ら] • 改造した Java 仮想機械上で変更を行う機構 • 特別なクラスとクラスローダーを定義 • 一般的に利用されている標準 Java 仮想機械を用いた上で実現する方法が望ましい • Envelope-Based weaving [‘05 Bockisch ら] • 事前にメンバーを準備しておく手法 • Aspect 指向言語の織り込みを支援 • クラス定義の変更には対応していない
まとめ • 標準 Java 仮想機械上で動的にメンバーの追加を行う機構を提案 • ロード時に各クラスにメンバーを準備 • 準備しておいたメソッドを利用して、型変換のコードを含め追加 • 呼び出し側の変換 • オーバライドの考慮 • マイクロベンチマークによる実験 • オーバーヘッドの計測