编译器(或JVM)是​​否知道优化这段代码?我该如何检查?

时间:2013-12-16 21:12:21

标签: java compiler-construction compiler-optimization

假设我有这个课程

Util
{
    public static void doSomething()
    {
         if (FLAG) foo();
         else bar();
    }

    public static void foo() { /* do something */ }
    public static void bar() { /* do something else */ }

    public static final boolean FLAG = computeFlag();
    private static boolean computeFlag() { /* do some computation during init time*/ }
}

FLAG显然永远不会改变。 假设使用Util.doSomething() A LOT (并且在许多关键位置,性能确实很重要)。 Java编译器或JVM是否足够智能以缓存doSomething的主体,以便代码不必重新评估FLAG或者必须重新执行分支指令?< / p>

我该如何检查?

由于

2 个答案:

答案 0 :(得分:4)

这可能取决于您使用的JVM。对于Oracle Hotspot JVM,您可以使用

检查生成的机器代码
java -server -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly your.MainClass

如果您在库路径中有必要的本机库(documentation提及您可以获取此类二进制文件。)

运行课程:

public class DecompileTest {

    public static void doSomething() {
        if (FLAG)
            foo();
        else
            bar();
    }

    static int fooCount;

    public static void foo() {
        fooCount++;
    }

    public static void bar() {
        fooCount--;
    }

    public static final boolean FLAG = computeFlag();

    private static boolean computeFlag() {
        System.out.println("Shall I set the flag? (y/n)");
        try {
            return System.in.read() == 'y';
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 1000000; i++) {
            doSomething();
        }
        System.out.println(fooCount);
    }   
}

java version "1.7.0_21"
Java(TM) SE Runtime Environment (build 1.7.0_21-b11)
Java HotSpot(TM) Server VM (build 23.21-b01, mixed mode)

在我的英特尔cpu上产生了一个冗长的disassabmly,相关部分读取:

Decoding compiled method 0x009ca408:
Code:
[Entry Point]
[Verified Entry Point]
[Constants]
  # {method} 'doSomething' '()V' in 'stackoverflow/DecompileTest'
  #           [sp+0x10]  (sp of caller)
  0x009ca500: sub    $0xc,%esp
  0x009ca506: mov    %ebp,0x8(%esp)     ;*synchronization entry
                                        ; - stackoverflow.DecompileTest::doSomething@-1 (line 8)
  0x009ca50a: mov    $0x295dc208,%ebx   ;   {oop(a 'java/lang/Class' = 'stackoverflow/DecompileTest')}
  0x009ca50f: incl   0x70(%ebx)         ;*putstatic fooCount
                                        ; - stackoverflow.DecompileTest::foo@5 (line 17)
                                        ; - stackoverflow.DecompileTest::doSomething@6 (line 9)
  0x009ca512: add    $0x8,%esp
  0x009ca515: pop    %ebp
  0x009ca516: test   %eax,0x950000      ;   {poll_return}
  0x009ca51c: ret    
  0x009ca51d: hlt    
  0x009ca51e: hlt    
  0x009ca51f: hlt    
[Exception Handler]
[Stub Code]
  0x009ca520: jmp    0x009c78c0         ;   {no_reloc}
[Deopt Handler Code]
  0x009ca525: push   $0x9ca525          ;   {section_word}
  0x009ca52a: jmp    0x009ae280         ;   {runtime_call}
  0x009ca52f: hlt    

也就是说,FLAG的测试和bar()的调用都已作为死代码被删除,而foo的方法体已内联。

答案 1 :(得分:1)

是的,这种代码是HotSpot消除死代码的简单目标。即使FLAG可能发生变化,HotSpot也会通过分析来确定,在实际执行中,它总是相同的,并消除未被删除的分支。

参考:PerformanceTacticIndex