为什么Java编译器没有优化一个简单的方法?

时间:2016-02-13 19:28:07

标签: java javac bytecode compiler-optimization

为说明目的,我有一个简单的课程:

public class Test {

    public int test1() {
        int result = 100;
        result = 200;
        return result;
    }

    public int test2() {
        return 200;
    }
}

编译器生成的字节码(由javap -c Test.class检查)如下:

public int test1();
Code:
   0: bipush        100
   2: istore_1
   3: sipush        200
   6: istore_1
   7: iload_1
   8: ireturn

public int test2();
Code:
   0: sipush        200
   3: ireturn

为什么编译器没有将test1方法优化为为test2方法生成的相同字节码?我希望它至少可以避免result变量的冗余初始化,因为很容易得出结论根本没有使用值100

我在Eclipse编译器和javac中都观察到了这一点。

javac版本:1.8.0_72,作为JDK的一部分与Java一起安装:

Java(TM) SE Runtime Environment (build 1.8.0_72-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.72-b15, mixed mode)

2 个答案:

答案 0 :(得分:4)

JVM优化字节码,创建称为 代码缓存 的东西。与C ++不同,JVM可以收集大量有关程序的数据,例如循环有多热?该代码块是否值得优化?等等。这里的优化非常有用,并且通常会产生更好的结果。

如果在从java转换为字节码时进行优化(即,当您调用 javac 时),您的代码可能是您的计算机的最佳选择,但不适用于某些不同的平台 。所以在这里进行优化是没有意义的。

作为示例,假设您的程序使用AES加密。现代CPU具有AES特定的指令集,使用特殊硬件使加密速度更快。

如果 javac 尝试在编译时进行优化,那么它将

  • 在软件级别优化指令,在这种情况下,您的程序不会受益于现代CPU,或
  • 将AES指令替换为等效的CPU-AES指令,仅在新CPU上支持,这将降低您的兼容性。

如果 javac 将它们保留在byptcode中,那么在较新的CPU上运行的JVM可以将它们识别为AES并利用此CPU功能,而在较旧的CPU上运行的JVM可以在运行时的软件级别(代码缓存),为您提供最优性兼容性

答案 1 :(得分:4)

典型的Java虚拟机在运行时优化您的程序,而不是在编译期间。在运行时,JVM对应用程序有更多了解,包括程序的实际行为以及程序执行的实际硬件。

字节代码仅仅是对程序应该如何表现的描述。运行时可以自由地对字节代码应用任何优化。

当然,人们可以争辩说,即使在编译过程中也可以应用这种简单的优化,但总的来说,不通过几个步骤分配优化是有意义的。任何优化都会有效地导致关于原始程序的信息丢失,这可能使其他优化变得不可能。这说,并非所有"最好的优化"总是显而易见的。一个简单的方法是在编译期间简单地删除(几乎)所有优化,并在运行时应用它们。