8

私はJavaの最適化についてすべてを理解しようとしていて、何か面白いものを見つけました。

最初のケース:プリミティブ型のコンパイル時の最適化

public class Clazz {
    public static void main(String args[]) {
        final int i = 300;
        new Clazz() {
            void foo() {
                System.out.println(i);
            }
        }.foo();
    }
}

コンパイル後(私はjd-gui-0.3.5.windowsバイナリファイルを逆コンパイルするために使用しています)、次のようになります。

public class Clazz {
    public static void main(String[] args) {
        int i = 300;
        new Clazz() {
            void foo() {
                System.out.println(300);
            }
        }.foo();
    }
}

さすが、そうではありませんか?iコンパイル後にその値(インライン最適化)に置き換えられました。したがって、プリミティブ型をラッパーに置き換えた後、似たようなものが表示されると思いましたが...

2番目のケース:非プリミティブ型のコンパイル時の最適化

public class Clazz {
    public static void main(String args[]) {
        final Integer i = 300; // replaced int with Integer
        new Clazz() {
            void foo() {
                System.out.println(i);
            }
        }.foo();
    }
}

コンパイル後:

public class Clazz {
    public static void main(String[] args) {
        Integer i = Integer.valueOf(300);
        new Clazz() {
            void foo() {
                System.out.println(Clazz.this);
            }
        }.foo();
    }
}

質問:

Clazz.thisこの文脈では何ですか?のインスタンスを囲むことへの参照であることは知っていますがClazz、その場合は機能しないはずです!印刷する必要がありますiが、コンパイラがClazz.this代わりに印刷するように提案し、機能します。何が問題ですか?正しく逆コンパイルされませjd-guiんか、それともJavaのコンパイルと最適化について何かが足りませんか?

UPD:

内容Class$1

class Clazz$1 extends Clazz {
    Clazz$1(Integer paramInteger) {}

    void foo() {
        System.out.println(this.val$i);
    }
}
4

3 に答える 3

8

正しく逆コンパイルされませjd-guiんか、それともJavaのコンパイルと最適化について何かが足りませんか?

jd-guiコードを誤って逆コンパイルしています。

私のJVMでは、匿名クラスの逆アセンブルされたコードは次のようになります。

class Clazz$1 extends Clazz {
  Clazz$1(java.lang.Integer);
    Code:
       0: aload_0       
       1: aload_1       
       2: putfield      #10                 // Field val$i:Ljava/lang/Integer;
       5: aload_0       
       6: invokespecial #12                 // Method Clazz."<init>":()V
       9: return        

  void foo();
    Code:
       0: getstatic     #20                 // Field java/lang/System.out:Ljava/io/PrintStream;
       3: aload_0       
       4: getfield      #10                 // Field val$i:Ljava/lang/Integer;
       7: invokevirtual #26                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      10: return        
}

ご覧のとおりi、匿名クラスのと呼ばれるフィールドに保存されているコピーval$i(名前は実装固有です)。

デコンパイラが誤ってとしてレンダリングするように見えるのは、このフィールドですClazz.this

于 2013-03-25T11:17:05.690 に答える
4

バイトコード(javap -c Clazz$1.class)を簡単に確認できます。

int i = 300

void foo();
   0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
   3: sipush        300
   6: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
   9: return

Integer i = 300

void foo();
   0: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
   3: aload_0
   4: getfield      #1                  // Field val$i:Ljava/lang/Integer;
   7: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
  10: return

したがって、intはインライン化されていますが、そうでIntegerはありません。


また、記録のために、これは私がjd-gui(3.0.5)から得たものです:

public static void main(String[] args) {
  Integer i = Integer.valueOf(300);
  new Clazz() {
    void foo() {
      System.out.println(this.val$i);
    }
  }
  .foo();
}
于 2013-03-25T11:22:11.750 に答える
0

これは、呼び出されたときにオブジェクトがその値をどのようにアウトボックス化jd-guiするかを解釈できない最も近いアプローチである必要があります。逆コンパイルアルゴリズム自体は、このオブジェクトへの最も重要な参照を推定する必要があり、の結果を選択します。IntegerSystem.out.printlnIntegerClazz.this

于 2013-03-25T11:25:40.547 に答える