33

invokespecialJVM 命令は、新しいオブジェクトを作成するときに初期化メソッド ( ) を呼び出すために使用されます<init>。命令の説明は、スーパークラスのコンストラクターを呼び出すか、現在のクラスのコンストラクターを呼び出すかの決定は、ファイルACC_SUPER内に設定されたフラグの状態に依存することを示唆しています (ただし、明確にはしていません)。class

Sun JVM 仕様から:

次に、次の条件がすべて真でない限り、解決されたメソッドが呼び出し用に選択されます。

  • ACC_SUPER フラグ (表 4.1「クラス アクセスとプロパティ修飾子」を参照) が現在のクラスに設定されています。

--ソース(invokespecialオペコード定義)

ACC_SUPER フラグの設定は、invokespecial 命令の 2 つの代替セマンティクスのどちらを Java 仮想マシンが表現するかを示します。ACC_SUPER フラグは、Java プログラミング言語用の Sun の古いコンパイラによってコンパイルされたコードの後方互換性のために存在します。Java 仮想マシンのすべての新しい実装は、この仕様に記載されている invokespecial のセマンティクスを実装する必要があります。Java 仮想マシンの命令セットに対する新しいコンパイラはすべて、ACC_SUPER フラグを設定する必要があります。Sun の古いコンパイラは、ACC_SUPER が設定されていない ClassFile フラグを生成しました。Sun の古い Java 仮想マシンの実装では、フラグが設定されていても無視されます。

--ソース(ClassFile形式)

定義では、フラグは古いコンパイラとの下位互換性を保つためのものであると述べています。しかし、それは矛盾し続けますSun's older Java virtual machine implementations ignore the flag if it is set.

フラグはまだinvokespecialオペコードで使用されていますか? 私が知る限り、それは何の目的も持たないように思われ、それがかつてあったことを示唆するリソースを見つけることができません.

ありがとう。

4

1 に答える 1

45

ACC_SUPERは、スーパーメソッドの呼び出しに関する問題を修正するために導入されました。ACC_SUPERフラグは、オペコード183命令の変更されたセマンティクス用にコンパイルされたものとしてクラスをマークします。その目的は、クラスがその命令の古いセマンティクス用にコンパイルされたか新しいセマンティクス用にコンパイルされたかをJVMが検出できるようにするため、クラスファイルのバージョン番号の目的と似ています。Java 1.0.2はACC_SUPERを設定および無視しませんでしたが、Java1.1以降は常にACC_SUPERを設定します。

Java 1.1より前では、現在呼び出されているオペコード183のバイトコード命令invokespecialが呼び出されinvokenonvirtual、仕様が部分的に異なっていました。これは、仮想メソッドルックアップなしでインスタンスメソッドを呼び出す必要がある場合に常に使用されました。これは、プライベートメソッド、インスタンス初期化子(コンストラクター)、およびにメソッド呼び出しを実装する場合に当てはまりましたsuper。しかし、後者の場合、進化するクラスライブラリで問題が発生しました。

バイトコード(CONSTANT_Methodref_info)のメソッド参照は、メソッドの名前と引数、および戻り値の型だけでなく、メソッドが属するクラスも定義します。オペコード183は、このようなメソッド参照パラメーターを取得し、さらにルックアップすることなく、指定されたクラスから参照されるメソッドを直接呼び出すことを目的としていました。呼び出しの場合、superこのメソッドを実装する最も近いスーパークラスを解決し、バイトコードへの参照を生成するのはコンパイラの責任でした。

Java 1.1以降、で参照されているクラスを本質的に無視し、CONSTANT_Methodref_info代わりに、JVMで指定されたメソッド名とシグネチャを使用して最も近いスーパーメソッドを検索するように変更されました。これは通常、クラスがロードされるとき、または命令が実行される直前、またはJITが最初にコンパイルされる直前に実行されます。

この変更が必要だった理由の例を次に示します。Java 1.0.2では、AWTクラスは次のように定義されていましたContainerComponent

class Component
{
    public void paint( Graphics g ) {}
}

class Container extends Component
{
    // inherits paint from Component but doesn't override it
}

Java 1.1では、クラスContainerが変更され、独自の実装が行われましたpaint

class Container extends Component
{
    public void paint( Graphics g ) {/*...*/}
}

これで、その直接または間接のサブクラスがContainer呼び出されsuper.paint(g)、1.0.2用にコンパイルされたときに、これがこのメソッドを持つ最初の親であるinvokenonvirtualため、の命令が生成されました。ただし、このコンパイル済みクラスを、それがまだ呼び出されてComponent.paintいるJVMで使用した場合、これは期待したものではありません。Container.paintComponent.paint

一方、1.1のクラスをコンパイルし、1.0.2 JVMで実行すると、AbstractMethodErrorがスローされるか、その時代のVMでは単にクラッシュする可能性が高くなります。クラッシュを回避するには((Component)super).paint(g)、いずれかのVMで目的の動作を実現するために、1.1コンパイラで記述およびコンパイルする必要がありました。これによりACC_SUPERが設定されますが、それでもを呼び出すための命令が生成されますComponent.paint。1.0.2 VMはACC_SUPERを無視して直接呼び出しComponent.paintますが、1.1 VMはACC_SUPERセットを検出しContainer.paint、バイトコードメソッド参照がであったとしてもそれを呼び出すルックアップ自体を実行しますComponent.paint

これについての詳細は、ikvm.netウェブログのこの古い投稿で見つけることができます。

于 2012-01-21T04:07:33.953 に答える