Oracle和Eclipse编译器生成的Java字节码的区别

13

我们的项目进行了一些Java字节码操作。然后我们发现了一些奇怪的行为。假设下面是代码片段:

  public void a() {
    new Integer(2);
  }

Oracle的javac编译以上代码为以下字节码:

   0:   new #2; //class java/lang/Integer
   3:   dup
   4:   iconst_2
   5:   invokespecial   #3; //Method java/lang/Integer."<init>":(I)V
   8:   pop
   9:   return

并将Eclipse的编译器转换为:

   0:   new #15; //class java/lang/Integer
   3:   iconst_2
   4:   invokespecial   #17; //Method java/lang/Integer."<init>":(I)V
   7:   return

你可以看到,Oracle编译器在"new"之后生成了"dup",而Eclipse没有。在这种情况下完全正确,因为新创建的Integer实例根本没有被使用,所以不需要"dup"。

我的问题是:

  1. 有没有一些关于不同编译器之间差异的概述?一篇文章或博客帖子?
  2. 如果在"new"和"invokespecial"之间没有"dup",我能否安全地得出结论,对象在初始化后未被使用?

Note: I have translated the text as requested while retaining the HTML tags and without adding any explanations.

4
你使用字节码仪器的目标是什么?这种差异会给你带来问题吗?注意,无法保证Java编译器生成的字节码完全相同。有可能在Oracle的javac的未来版本中会产生不同于现在的字节码 - 因此撰写过于依赖编译器生成的确切字节码的程序并不是一个好主意。 - Jesper
你在Eclipse中使用不同的JDK吗? - Nishant
3个回答

6
如果在newinvokespecial之间有一个dup,那么该对象通常会在编译后使用。例如,字段初始化通常是一个newdupinvokespecialputfield序列。然而,在您的示例中,最后一条指令是pop,它清除了堆栈中的对象引用——这就是您可以假设该对象未被使用的原因。

正如A.H.所指出的那样,pop仅意味着调用者未使用它。对象本身可能在构造函数中透露对自身的引用。 - Antimony

3
我不确定您的确切意思,但构造函数可能会将所创建对象的引用存储在某个位置。因此,调用方法在初始化后可能不再使用该对象,但该对象仍然可以被访问,可能无法被垃圾收集。

1

传递this引用会稍微打破这个模式

  public class Bump {

    Test t;

    public Bump() {
        new Test(this);
    }
    public void setT(Test t) {
        this.t = t;
    }
  }

然后可以使用this将结果存储回去 :)

  public class Test {

    Bump b;

    public Test(Bump b) {
        this.b = b;
        b.setT(this);
    }
  }

玩得开心 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接