12

リフレクションを使用していますか?もしそうなら、舞台裏で何が起こっていますか?

4

2 に答える 2

13

Jon は、演算子がバイトコードにどのようにマップされるかについて正しいです。実装に関する限り、ほとんどの JVM はメモリ内のオブジェクトを、ロードされた具象クラスのタグ付き共用体として表します。

タグ付き共用体は、バリアント、バリアント レコード、判別共用体、非結合共用体、または合計型とも呼ばれ、いくつかの異なるが固定された型を取ることができる値を保持するために使用されるデータ構造です。

そのため、具象型がクラス型のインスタンスである場合にビットが設定されたスパースブール行列x instanceof MyClassTypeを調べることで答えることができます。

x instanceof InterfaceTypeは少しトリッキーですが、同様の方法でそれを解決することもできます。

JVM は、名目上の型 (クラスまたはインターフェイス型) ごとに行を 1 つ、クラス型ごとに列を 1 つ持つ大規模なスパース マトリックスをメモリ内に保持できます。

例えば:

                           [all nominal types]
                     Object String Integer Number Comparable Iterable ...
[only       String   ✓      ✓                     ✓
 concrete   Integer  ✓             ✓       ✓      ✓
 types]     ...

JVM がクラスをガベージ コレクションする必要がある場合、このマトリックスを維持するのが難しくなるため、通常はクラス オブジェクトを含む行を格納します。


プロキシ クラスは興味深い特殊なケースですが、私の推測では、プロキシ クラスの定義には、実行時に何らかのバイトコードを生成し、その後、ほとんどの JVM で通常のクラス ローディング システムを通過させるというものがあります。

于 2013-10-05T15:09:09.877 に答える
11

基本的に、これは JVM 命令セットの一部です。特定のinstanceof命令があります。たとえば、次のようなメソッドです。

public static void checkString(Object x) {
    if (x instanceof String) {
        System.out.println("Foo");
    }
}

次のようにコンパイルされます。

public static void checkString(java.lang.Object);
  Code:
     0: aload_0
     1: instanceof    #2                  // class java/lang/String
     4: ifeq          15
     7: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
    10: ldc           #4                  // String Foo
    12: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
    15: return
}

(それは単なる出力ですjavap。)

JVM 仕様には、命令が何をしなければならないかの詳細があります。命令の正確な詳細については、セクション 6.5を参照してください。実装方法は VM の実装次第ですが、実装例の 1 つとして次のようなものがあります。

  • 最初のオペランドが であるかどうかを確認しますnull(そうであれば戻りfalseます)
  • 最初のオペランドが参照するオブジェクトの実行時の型を見つけます。
  • 実際の型が 2 番目のオペランドと互換性があるかどうかを証明できるまで、型階層 (実装されたインターフェイスを含む) を上に移動します。
于 2013-10-05T15:01:10.727 に答える