11

私は最近、JVMバイトコードで操作を実行するライブラリを開発しているときに、ドキュメントがない(私が見つけた)いくつかのオペコードに出くわしましたが、JVMリファレンス実装によって認識されています。これらのリストを見つけました。それらは次のとおりです。

BREAKPOINT = 202;
LDC_QUICK = 203;
LDC_W_QUICK = 204;
LDC2_W_QUICK = 205;
GETFIELD_QUICK = 206;
PUTFIELD_QUICK = 207;
GETFIELD2_QUICK = 208;
PUTFIELD2_QUICK = 209;
GETSTATIC_QUICK = 210;
PUTSTATIC_QUICK = 211;
GETSTATIC2_QUICK = 212;
PUTSTATIC2_QUICK = 213;
INVOKEVIRTUAL_QUICK = 214;
INVOKENONVIRTUAL_QUICK = 215;
INVOKESUPER_QUICK = 216;
INVOKESTATIC_QUICK = 217;
INVOKEINTERFACE_QUICK = 218;
INVOKEVIRTUALOBJECT_QUICK = 219;
NEW_QUICK = 221;
ANEWARRAY_QUICK = 222;
MULTIANEWARRAY_QUICK = 223;
CHECKCAST_QUICK = 224;
INSTANCEOF_QUICK = 225;
INVOKEVIRTUAL_QUICK_W = 226;
GETFIELD_QUICK_W = 227;
PUTFIELD_QUICK_W = 228;
IMPDEP1 = 254;
IMPDEP2 = 255;

それらは他の実装の代替品のように見えますが、異なるオペコードを持っています。Google でページを次々と探し回った後、このドキュメントLDC*_QUICKでオペコードについての言及を見つけました。

LDC_QUICKオペコードでそれから引用します:

操作定数プールから項目をプッシュ

フォーム ldc_quick = 203 (0xcb)

積み上げ……、アイテム

説明インデックスは、現在のクラス (§3.6) の定数プールへの有効なインデックスでなければならない符号なしバイトです。index の定数プール項目は既に解決されている必要があり、1 ワード幅でなければなりません。アイテムは定数プールからフェッチされ、オペランド スタックにプッシュされます。

この命令のオペコードはもともと ldc で​​した。ldc 命令のオペランドは変更されません。

大丈夫。面白そうだったので、やってみることにしました。LDC_QUICKと同じ形式のように見えるので、オペコードをLDCに変更しました。これは失敗に終わりましたが、JVM は明らかにそれを認識していました。変更されたファイルを実行しようとした後、JVM は次の出力でクラッシュしました。LDCLDC_QUICK

Exception in thread "main" java.lang.VerifyError: Bad instruction: cc
Exception Details:
  Location:
    Test.main([Ljava/lang/String;)V @9: fast_bgetfield
  Reason:
    Error exists in the bytecode
  Bytecode:
    0000000: bb00 0559 b700 064c 2bcc 07b6 0008 572b
    0000010: b200 09b6 000a 5710 0ab8 000b 08b8 000c
    0000020: 8860 aa00 0000 0032 0000 0001 0000 0003
    0000030: 0000 001a 0000 0022 0000 002a b200 0d12
    0000040: 0eb6 000f b200 0d12 10b6 000f b200 0d12
    0000050: 11b6 000f bb00 1259 2bb6 0013 b700 14b8
    0000060: 0015 a700 104d 2cb6 0016 b200 0d12 17b6
    0000070: 000f b1
  Exception Handler Table:
    bci [84, 98] => handler: 101
  Stackmap Table:
    append_frame(@60,Object[#41])
    same_frame(@68)
    same_frame(@76)
    same_frame(@84)
    same_locals_1_stack_item_frame(@101,Object[#42])
    same_frame(@114)

        at java.lang.Class.getDeclaredMethods0(Native Method)
        at java.lang.Class.privateGetDeclaredMethods(Unknown Source)
        at java.lang.Class.getMethod0(Unknown Source)
        at java.lang.Class.getMethod(Unknown Source)
        at sun.launcher.LauncherHelper.validateMainClass(Unknown Source)
        at sun.launcher.LauncherHelper.checkAndLoadMain(Unknown Source)

上記のエラーでは、さまざまなメッセージが表示されます。明らかに、クラス ファイルの検証に失敗しました: java.lang.VerifyError: Bad instruction: cc. 同時に、JVM はオペコードを認識しました: @9: fast_bgetfieldfast_bgetfieldまた、一定のプッシュを意味するものではないため、別の命令であると考えているようです...

私はかなり混乱していると言っても過言ではないと思います。これらの違法なオペコードは何ですか? JVMはそれらを実行しますか? VerifyErrorsを受け取っているのはなぜですか? 非推奨?そして、彼らは文書化された対応物よりも利点がありますか?

どんな洞察も大歓迎です。

4

3 に答える 3

13

Java 仮想マシン仕様の初版では、バイトコードの解釈を高速化するために、Sun の Java 仮想マシンの初期の実装の 1 つで使用された手法について説明しました。この方式では、定数プール エントリを参照するオペコードは、定数プール エントリが解決されるときに「_quick」オペコードに置き換えられます。仮想マシンが _quick 命令に遭遇すると、定数プール エントリが既に解決されていることを認識するため、命令をより高速に実行できます。

Java 仮想マシンのコア命令セットは、200 のシングルバイト オペコードで構成されています。これらの 200 のオペコードは、クラス ファイルに表示される唯一のオペコードです。「_quick」手法を使用する仮想マシンの実装は、別の 25 個のシングルバイト オペコード「_quick」オペコードを内部的に使用します。

たとえば、_quick 手法を使用する仮想マシンが、ldc 命令 (オペコード値 0x12) によって参照される定数プール エントリを解決する場合、バイトコード ストリーム内の ldc オペコード バイトを ldc_quick 命令 (オペコード値 0xcb) に置き換えます。この手法は、Sun の初期の仮想マシンでシンボリック参照を直接参照に置き換えるプロセスの一部です。

一部の命令では、_quick オペコードで通常のオペコードを上書きするだけでなく、_quick 手法を使用する仮想マシンは、直接参照を表すデータで命令のオペランドを上書きします。たとえば、invokevirtual オペコードを invokevirtual_quick に置き換えるだけでなく、仮想マシンは、すべての invokevirtual 命令に続く 2 つのオペランド バイトにメソッド テーブル オフセットと引数の数を入れます。invokevirtual_quick オペコードに続くバイトコード ストリームにメソッド テーブル オフセットを配置すると、仮想マシンは、解決された定数プール エントリでオフセットを検索するのにかかる時間を節約できます。

Java 仮想マシンの内部の第 8 章

基本的に、オペコードをクラスファイルに入れることはできません。オペランドを解決した後、JVM だけがそれを行うことができます。

于 2013-02-12T00:22:45.223 に答える
4

リストされたすべてのオペコードについてはわかりませんが、そのうちの 3 つ (<em>breakpoint、impdep1、およびimpdep2 ) は、Java 仮想マシン仕様のセクション 6.2 に記載されている予約済みオペコードです。部分的に次のように述べています。

254 (0xfe) と 255 (0xff) の 2 つの予約済みオペコードには、それぞれimpdep1impdep2というニーモニックがあります。これらの命令は、ソフトウェアとハ​​ードウェアにそれぞれ実装された実装固有の機能に「バックドア」またはトラップを提供することを目的としています。3 番目の予約済みオペコードである番号 202 (0xca) にはニーモニックブレークポイントがあり、デバッガがブレークポイントを実装するために使用することを目的としています。

これらのオペコードは予約されていますが、Java 仮想マシンの実装内でのみ使用できます。これらは、有効なクラス ファイルには表示されません。. . .

私は (それらの名前から) 残りのオペコードは JIT メカニズムの一部であり、有効なクラス ファイルにも表示されないのではないかと考えています。

于 2013-02-11T23:10:26.000 に答える
3

これらのオペコードは予約されており、有効なクラス ファイルに表示できないため、VerifyError が発生します。ただし、JVM はそれらを内部で使用します。したがって、一部のバイトコードのメモリ内表現には、VM による変更後にこれらのオペコードが含まれる場合があります。ただし、これは純粋に実装の詳細です。

于 2013-02-11T23:39:47.890 に答える