javac 是否优化 "foo".length()?
Does javac optimize "foo".length()?
如果我在代码中有这个statement/literal:
"foobar".length()
编译器换成6了吗?或者它是否调用内部化实例的方法?将这样的内容放在代码中比无意义的 6 更具可读性,但我不想一遍又一遍地调用该方法,即使我知道它只是一种 getter 用于私有字段。
如果是,它是由某种 javac 白名单完成的,哪些方法在编译期间可以安全地评估哪些文字(或者可能是所有不带任何参数且不依赖于的方法)来自环境的状态(?))可能的副作用等等
它没有被 javac 取代:在 Java 字节码中你会看到对 length()
方法的显式调用。然而,在 JIT 编译期间,它可能会被常量替换,因为 JIT 编译器足够聪明,可以内联 length()
方法并检测它 returns 常量字符串的最终数组字段的长度。
总的来说Java 字节码的优化很少。大部分艰苦的工作是在 JIT 编译期间完成的。不过,您不必担心这种情况下的性能:热代码将进行 JIT 编译。
为了证明我的答案,这里有一个简单的方法:
public static long lengthTest() {
long sum = 0;
for(int i=1; i<="abcdef".length(); i++) sum+=i*2;
return sum;
}
这是字节码:
0: lconst_0
1: lstore_0
2: iconst_1
3: istore_2
4: iload_2
5: ldc #5 // String abcdef
7: invokevirtual #6 // Method java/lang/String.length:()I
10: if_icmpgt 26
13: lload_0
14: iload_2
15: iconst_2
16: imul
17: i2l
18: ladd
19: lstore_0
20: iinc 2, 1
23: goto 4
26: lload_0
27: lreturn
如您所见,有一个显式调用 length()
。
这是 JIT 编译代码 (x64 ASM):
sub [=12=]x18,%rsp
mov %rbp,0x10(%rsp) ;*synchronization entry
; - Test::lengthTest@-1 (line 12)
mov [=12=]x2a,%eax
add [=12=]x10,%rsp
pop %rbp
test %eax,-0x260e95c(%rip) # 0x0000000000330000
; {poll_return}
retq
如您所见,整个方法体在技术上被替换为单个常量(mov [=16=]x2a,%eax
,0x2a 为 42,这是该方法的实际结果)。所以 JIT 编译器不仅内联 length()
,还将整个方法体计算为常量!
如果我在代码中有这个statement/literal:
"foobar".length()
编译器换成6了吗?或者它是否调用内部化实例的方法?将这样的内容放在代码中比无意义的 6 更具可读性,但我不想一遍又一遍地调用该方法,即使我知道它只是一种 getter 用于私有字段。
如果是,它是由某种 javac 白名单完成的,哪些方法在编译期间可以安全地评估哪些文字(或者可能是所有不带任何参数且不依赖于的方法)来自环境的状态(?))可能的副作用等等
它没有被 javac 取代:在 Java 字节码中你会看到对 length()
方法的显式调用。然而,在 JIT 编译期间,它可能会被常量替换,因为 JIT 编译器足够聪明,可以内联 length()
方法并检测它 returns 常量字符串的最终数组字段的长度。
总的来说Java 字节码的优化很少。大部分艰苦的工作是在 JIT 编译期间完成的。不过,您不必担心这种情况下的性能:热代码将进行 JIT 编译。
为了证明我的答案,这里有一个简单的方法:
public static long lengthTest() {
long sum = 0;
for(int i=1; i<="abcdef".length(); i++) sum+=i*2;
return sum;
}
这是字节码:
0: lconst_0
1: lstore_0
2: iconst_1
3: istore_2
4: iload_2
5: ldc #5 // String abcdef
7: invokevirtual #6 // Method java/lang/String.length:()I
10: if_icmpgt 26
13: lload_0
14: iload_2
15: iconst_2
16: imul
17: i2l
18: ladd
19: lstore_0
20: iinc 2, 1
23: goto 4
26: lload_0
27: lreturn
如您所见,有一个显式调用 length()
。
这是 JIT 编译代码 (x64 ASM):
sub [=12=]x18,%rsp
mov %rbp,0x10(%rsp) ;*synchronization entry
; - Test::lengthTest@-1 (line 12)
mov [=12=]x2a,%eax
add [=12=]x10,%rsp
pop %rbp
test %eax,-0x260e95c(%rip) # 0x0000000000330000
; {poll_return}
retq
如您所见,整个方法体在技术上被替换为单个常量(mov [=16=]x2a,%eax
,0x2a 为 42,这是该方法的实际结果)。所以 JIT 编译器不仅内联 length()
,还将整个方法体计算为常量!