「Javaのプログラムはどうやって動いているの?」古いパズルを試した。

【初学者向け】JJUG ナイトセミナ 「Javaのプログラムはどうやって動いているの?」

https://www.youtube.com/watch?t=23&v=QNJBcrSayME

これを見てずっと前にさっぱり理解できなかったパズルがあって今なら少しだけなら解るような気がするかもしれないと思い試してみた。

元ネタは JAVA PUZZLERS の優れたパズラー パズル44:授業をサボる(Cutting Class)です。

このパズルを元に試してみたのですが時既に遅し・・・

Java SE 8 では既にパズルにはならなくなっていました。(Java SE 6 から)

なので、J2SE5.0 を使うことにしました。(ヲヒ

今回このパズルを試すのに組んだコードはこれです。

a

Love クラスはコンパイル時には存在していてちゃんとコンパイルされるものとします。

ただし、このプログラムの実行時には Love クラスは消えていることとします。

さて、どういった実行結果になるか想像できますか?

Lost Love が表示されるように思われますが残念な結果になります。

衝撃の事実をご覧ください。

3

なんでこうなるの?

バイトコードを覗いてみます。

4

バイトコードの怪しそうなところを見ていきましょう。

11: astore_1 とmain()メソッドのコードにあるのが問題の原因です。

これは catch ブロックでキャッチされたキャッチパラメータ e を VM 変数 1 に保存しています。

既に 7: astore_1 と Love クラスのローカル変数 love が VM 変数1に保存されています。

VM 変数1に上述の二つがマッピングされています。

このプログラムを実行すると JVM が起動、そしてクラスのロード、リンク、初期化、main 実行となります。

このプログラムはリンクのベリフィケーションで例外が発生してしまっています。

VM 変数1にマッピングされている二つのクラスをベリファイアはマージしようとする時にベリファイアは Love クラスのスーパークラスを決定するために Love クラスをロードしようとします。

しかし、 Love クラスは消えているのでロードに失敗してしまって NoClassDefFoundError がスローされます。

悲しいことにこの例外はベリフィケーション中にスローされていますのでクラスの初期化される前、main実行開始前ということでこのような残念なことになってしまいます。

本当に VM 変数1へのマッピングだけが問題かどうか確かめてみます。

プログラムを次のように変更します。

Love クラスのローカル変数の宣言を try – catch ブロックの外に出します。

b

プログラムを実行させて確認します。

c1

期待通りの動作となって Lost Love と出力されています。

キャッチパラメータ e も VM 変数 2 にマッピングされています。

11: astore_2

これでリンクのベリフィケーションも解決されて初期化、main実行で catch ブロックが実行されました。

このエントリーの冒頭にも記述しましたが現在の Java ではこのような問題は発生しません。

おそらくリンクの処理が賢くなってしまったんでしょう。

ついでだからプログラムのコードを戻して Java SE 6 で確認しておきます。

1

2

問題ないですね。

VM 変数1に Love クラスのローカル変数 love と catch ブロックでキャッチされたキャッチパラメータ e がマッピングされてもちゃんとベリフィケーションされるようになったようです。

JVM のことは詳しく解らないので間違っているかもしれませんがこうやってバイトコードを覗いてみるのもおもしろいですね。

例外テーブルの動作も表示されているし、BASIC のような goto 文まで。

詳しくは元ネタの本を読んでください。(^_^;)

今日の教訓:「Love が消えてしまうと悲しい!」

違う!

今日の教訓:「古い Java より新しい Java で幸せに!」

  

Hatena タグ:

「Javaのプログラムはどうやって動いているの?」を試してみた

【初学者向け】JJUG ナイトセミナ 「Javaのプログラムはどうやって動いているの?」

https://www.youtube.com/watch?t=23&v=QNJBcrSayME

これを実際に試してみた。

a1

javap を使ってバイトコードの逆アセンブルをする。

javap –p –c クラス名

とすると–p オプションで private メソッドも対象になる。-c オプションを付けるとバイトコードも出力される。

やってみた。

a2

add() メソッドは動画で詳しく説明されていたので良く解る。

これでお終いではちょっとさみしいので Java 8 から導入されたインタフェースのデフォルトメソッドを使うようにプログラムを変更してみる。

b1

b2

Java 7 まではインタフェースにメソッドの実装は許されてなかった。

Java 8 からは default メソッドとして可能となりました。

いったいどうなっているのでしょうか?

javap で覗いてみることにします。今度は –v オプションを使ってバイトコードと定数プールも出力させてみます。

b3

b4

この結果から解るようにインタフェース側にメソッドの実装がされています。

おもしろいですね!

しかし、部分的にバイトコードを知ることができたのですがまだまだ解らないことだらけなので次回開催を楽しみに待ってます。

Hatena タグ: