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