これはおすすめ 意見交換/共有ツールとその理由

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 仮想機械上で
動的にメンバーの追加を行う機構を提案
▫ ロード時に各クラスにメンバーを準備
▫ 準備しておいたメソッドを利用して、
型変換のコードを含め追加
▫ 呼び出し側の変換
▫ オーバライドの考慮
• マイクロベンチマークによる実験
▫ オーバーヘッドの計測