Java编译器是否会在for循环条件下优化String.length()?

时间:2014-10-17 17:44:03

标签: java

考虑以下Java代码片段:

String buffer = "...";
for (int i = 0; i < buffer.length(); i++)
{
    System.out.println(buffer.charAt(i));
}

由于String是不可变的并且buffer未在循环中重新分配,因此Java编译器是否足够智能以优化for循环条件中的buffer.length()调用?例如,它是否会发出等效于以下的字节代码,其中buffer.length()被赋值给变量,并且该变量在循环条件中使用?我已经读过像C#这样的语言进行这种优化。

String buffer = "...";
int length = buffer.length();
for (int i = 0; i < length; i++)
{
    System.out.println(buffer.charAt(i));
}

3 个答案:

答案 0 :(得分:6)

在Java(和.Net)中,字符串是长度计数(UTF-16代码点的数量),因此查找长度是一个简单的操作。

编译器(javac)可能会也可能不会执行hoisting,但JVM JIT编译器将almost certainly内联调用.length(),使buffer.length()成为{{1}}只不过是一个内存访问。

答案 1 :(得分:1)

Java编译器(javac)不执行此类优化。 JIT编译器可能会内联length()方法,这至少可以避免方法调用的开销。

根据您正在运行的JDK,length()方法本身可能会返回最终length字段,这是一个便宜的内存访问,或字符串的长度内部char[]数组。在后一种情况下,数组的长度是常量,并且数组引用可能是final,因此JIT可能足够复杂,可以根据您的建议在临时中记录一次长度。但是,这种事情是一个实现细节。除非你控制你的代码将运行的每台机器,否则你不应该对它将运行哪个JVM做出太多假设,或者它将执行哪些优化。

至于如何编写代码,在循环条件中直接调用length()是一种常见的代码模式,并从可读性中受益。我保持简单并让JIT优化器完成其工作,除非您处于已证明性能问题的关键代码路径中,并且您同样证明了这样的微优化是值得的。

答案 2 :(得分:1)

您可以做几件事来检查实施的两种变体。

  1. (难度:容易)在每个版本的代码中,在类似条件下进行测试并测量速度。确保循环足够重要以注意差异,可能没有。

  2. (难度:中)用javap检查字节码并查看编译器如何解释两个版本(这可能因javac实现而异)或者可能不同(当规范中指定了行为并且没有实施者解释的余地​​。)

  3. (难度:难)用JITWatch检查两个版本的JIT输出,你需要非常了解字节码和汇编程序。

相关问题