3

如果我有两个在编译时已知的常量,Java 编译器会将它们折叠起来。

final static int foo = 2;
final static int bar = 17;
int myVariable;

int myFunction(){
    return foo*bar + myVariable;
}

在运行时,myFunction 将返回 34 + myVariable,并且不需要像在编译时那样计算 2*17。

我的问题是:如果直到运行时才知道常量,它会做同样的事情吗?我相信这被称为运行时代码专业化。

final int foo;
final int bar;
int myVariable;

int myFunction(){
    return foo*bar + myVariable;
}

如果 foo 和 bar 在对象的构造函数中被初始化为 2 和 17,myFunction 是否会专门返回 34 + myVariable,或者它是否仍会在每次调用函数时计算 foo*bar,即使 foo*bar 永远不会改变?

*编辑:我指的是最新版本的 JVM,1.7.0_45。

4

3 回答 3

7

Foo 和 Bar在那种情况下永远不会改变,尽管它们在不同的情况下很可能是不同的,并且系统不会为每个对象的每个实例单独编译代码。这样做的开销将是可怕的。

系统可能会检测到这一点,但不太可能,您当然不能指望所有甚至大多数环境都这样做。foobar = foo*bar如果你认为乘法会对性能产生重大影响,你最好的选择是存储自己并使用它。(这不太可能)。

于 2014-01-04T02:28:20.700 回答
4

我有 JDK 1.7.0_45,我编译了这段代码

class X {
    final int foo;
    final int bar;

    X() {
        foo = 2;
        bar = 17;
    }

    int myVariable;

    int myFunction() {
        return foo * bar + myVariable;
    }

并为 myFunction 得到了这个字节

myFunction()I
   L0
    LINENUMBER 22 L0
    ALOAD 0
    GETFIELD test/X.foo : I         <-- load 2
    ALOAD 0
    GETFIELD test/X.bar : I         <-- load 17
    IMUL                            <-- 2 x 17
    ALOAD 0
    GETFIELD test/X.myVariable : I
    IADD
    IRETURN

所以回答“每次调用函数时它会计算 foo*bar 吗?” 是的(除非 JVM 对其进行了优化)

于 2014-01-04T03:34:27.853 回答
2

热点编译的汇编器

mov r10d,DWORD PTR [rsi+0x8]
shl    r10,0x3
cmp    rax,r10
jne    0x00007fb509045b60        ;   {runtime_call}
data32 xchg ax,ax
nop    WORD PTR [rax+rax*1+0x0]
nop    WORD PTR [rax+rax*1+0x0]
[Verified Entry Point]
sub    rsp,0x18
mov    QWORD PTR [rsp+0x10],rbp  ;*synchronization entry
                                 ;- Main$TestClass::myFunction@-1 (line 25)

mov    eax,DWORD PTR [rsi+0x10]
imul   eax,DWORD PTR [rsi+0xc]
add    eax,DWORD PTR [rsi+0x14]  ;*iadd 
                                 ;- Main$TestClass::myFunction@13 (line 25)
add    rsp,0x10
pop    rbp
test   DWORD PTR [rip+0x15f7a8bf],eax        # 0x00007fb51f087000 
                                 ;{poll_return}
ret

Java 代码

public class Main {
    public static void main(String... args) {
        int accm = 0;
        TestClass t = new TestClass(542,452);
        while(true){
            t.myVariable = accm;
            accm += t.myFunction();
            if(false) break;
        }
        System.out.println(accm);
    }

    public static class TestClass{
        final int foo;
        final int bar;
        public int myVariable = 2;

        public TestClass(int foo, int bar){
            this.foo = foo;
            this.bar = bar;
        }


        int myFunction(){
            return foo*bar + myVariable;
        }
    }
}

Java版

java version "1.8.0-ea"
Java(TM) SE Runtime Environment (build 1.8.0-ea-b124)
Java HotSpot(TM) 64-Bit Server VM (build 25.0-b66, mixed mode)

结论

不,它不能。

虽然我不明白那里发生的大部分事情,但似乎该函数已被内联到循环中[Verified Entry Point],但常量没有折叠,因为在 处仍然发生乘法imul eax,DWORD PTR [rsi+0xc]

于 2014-01-26T23:15:17.463 回答