今日の発見

あ、コンパイル時は型を見ます。ただ、class-fileには型情報は引き継がれず、代入したときに型が確定します(仕様書を見ると「いかにも型情報を持っている」ように見えますが、実は中身は空です。おかげで1時間ほど無駄にしました)。型も

  • プリミティブ型(int,floatなど)
  • 配列型
  • その他クラス

という超アバウトな分け方。配列型はメンバを持ってるのにクラス扱いじゃないんですね(lengthはバイトコードに直接命令がある)。更に、局所変数は「番号」でしか管理してません。なので、バイトコードのスタックから引き継ぐ引数に「何番」という情報が入っていることもあります(直接JavaVMのアセンブリ言語を書くのなら、コンパイル時に確定しなくてもいい)。なので、局所変数すらも動的に切り替えられます。それどころか、「メソッド前半はString、後半はint」なんておかしな局所変数すらも作れます。
うわー、Javaってコンパイラは静的型にうるさいくせにVM動的言語も動かせるのか。
ちなみに、残念ながらフィールドはばっちり型情報を持っています。


しかし、こう考えるとAndroidのDEXはよくこれをレジスタに割り当ててるなぁ。実行時まで型が確定していないうえ、「変数番号」で見てるのに。ちなみに、Java2SEとDalvikでは実行時に参照される引数によるオーバーロードのルールがちょっと違います。なので、シグネチャを元に切り替えるようなメソッドの使い方をしていると動きの違いに悩むことに。いや、同じ名前のメソッド作るなということですが(JavaScriptなんかはオーバーロードに対応してない。というか、引数の型どころか数すらも不定)。
しかし、JavaVMのメソッド呼び出しの速さはシグネチャによってコンパイル時にある程度絞り込んでるおかげだと思っていたのですが、これを見る限り違いますね。ちゃんと動的に検索し直してます。検索結果を取っておくメソッド呼び出しキャッシュでも持ってるのかな? だとすると、ポリモーフィズム使いまくりのコードだと効率悪いのかなぁ?