有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

最终引用的优化在java中是如何工作的?

我一直在试图弄清楚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();
    }
}

正如所料,不是吗^编译后,{}被替换为它的值(内联优化)。所以,在用它的包装器替换原语类型之后,我希望看到类似的东西,但是

第二种情况:非原语类型编译时优化

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 Clazz$1 extends Clazz {
    Clazz$1(Integer paramInteger) {}

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

共 (3) 个答案

  1. # 1 楼答案

    您只需查看字节码(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();
    }
    
  2. # 2 楼答案

    Does jd-gui decompiles incorrectly or do I missing something about Java compilation and optimization?

    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

  3. # 3 楼答案

    这应该是一种最接近的方法,jd-gui无法解释Integer对象在调用System.out.println时如何装箱其值。反编译算法本身必须假定对这个Integer对象最重要的引用是什么,并选择Clazz.this的结果