【问题标题】:Does javac optimize "foo".length()?javac 是否优化“foo”.length()?
【发布时间】:2015-09-01 15:12:12
【问题描述】:

如果我在代码中有这个语句/文字:

"foobar".length()

是否被编译器替换为6?或者它是否调用内部实例的方法?将这样的内容放在代码中而不是无意义的 6 中更具可读性,但我不想一遍又一遍地调用该方法,即使我知道它只是私有字段的一种 getter。

如果是,它是由某种 javac 白名单完成的,哪些方法在编译期间可以安全地评估哪些文字(或者可能是所有不带任何参数且不依赖于状态的方法来自环境(?))可能​​的副作用等等。

【问题讨论】:

  • 你试过什么?您是否尝试读取编译器生成的中间代码?
  • 不,我不会说字节码,只是在谷歌上搜索并没有找到答案...我有一个想法,用一些 for 循环创建一个基准,并将 System.out.println("foobar".length())System.out.println(6) 进行比较,但是变量太多了..其中之一是JIT(我知道我可以关掉它)...更容易问
  • 我不会说字节码学习永远都不晚。

标签: java string jvm javac


【解决方案1】:

它不会被 javac 取代:在 Java 字节码中,您将看到对 length() 方法的显式调用。然而,在 JIT 编译期间,它可能会被常量替换,因为 JIT 编译器足够聪明,可以内联 length() 方法并检测到它返回常量字符串的最终数组字段的长度。

一般来说,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 $0x18,%rsp
mov %rbp,0x10(%rsp)  ;*synchronization entry
                     ; - Test::lengthTest@-1 (line 12)
mov $0x2a,%eax
add $0x10,%rsp
pop %rbp
test %eax,-0x260e95c(%rip)  # 0x0000000000330000
                            ;   {poll_return}
retq

正如你所见,整个方法体在技术上被替换为单个常量(mov $0x2a,%eax,0x2a 是 42,这是方法的实际结果)。因此,JIT 编译器不仅内联了length(),还将整个方法体计算为常量!

【讨论】:

  • @JiriKremser,编辑了答案,让它更加精彩!
猜你喜欢
  • 2014-08-28
  • 1970-01-01
  • 2011-06-27
  • 1970-01-01
  • 1970-01-01
  • 2017-07-18
  • 2015-12-25
  • 1970-01-01
  • 2015-12-22
相关资源
最近更新 更多