Java for循环优化

5
Java运行时对以下代码片段会进行什么样的优化?字节码并没有显示任何优化,但我认为Java应该在不运行整个for循环的情况下取最后一个for循环的值,因为String是一个基础的Java类。
注意:这个问题是在一次课堂测试中提出的,然而,我无法提供足够的证据来支持我的说法。
public class Main {

    public static void main(String[] args) {
        String str = null;
        for (long i = 0; i < 10000000000L; i++) {
            str = new String("T");
        }

        System.out.println(str);
    }
}

4
基本上没有对字节码级别进行优化;几乎所有的优化都是在运行时完成的。JIT(即时编译器)_大多数_是一个神奇的黑盒子,你不能保证任何事情,但整个循环被消除并非不可能。 - Louis Wasserman
您可以通过向JVM添加以下选项来查看JIT如何“处理”您的代码:-XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining - fge
@fge 解密那个输出结果相当具有挑战性。我并不完全确定发生了什么。 - Mathew Kurian
"would" 是一个奇怪的问题。"could" 可能会有趣。 - user4842163
1
这是一个不好的测试问题,因为它没有在任何地方指定,并且很难发现。而且还依赖于供应商和版本。可能会有零优化或消除循环的情况。 "String是Java中一个“基本”的类"这一事实在这种情况下并没有什么关系。 - user207421
显示剩余2条评论
1个回答

1
虽然我不能确切地说明jit编译器在做什么,但你要求它进行的优化(确定可以完全跳过循环体是安全的)实际上非常难以实现,因此我非常怀疑它会这样做。无论String是否是一个“基本的Java类”,都是如此。
为了更好地理解,首先让我们假设我们正在创建任意类Foo的实例,只有在我们知道两件事情时,才能安全地跳过所有这些Foo对象的创建:调用new Foo()没有任何可观察的副作用;并且没有对Foo的引用“逃逸”出循环体。
可观察的副作用可能是设置静态成员的值(例如,如果Foo类保留了所有Foo()被调用的次数的静态计数)。引用逃逸的一个例子是如果Foo()内部的this变量被传递到其他地方。
请注意,仅查看 Foo() 是不够的,您需要查看Foo的超类构造函数(一直到Object)。然后,您需要查看初始化每个这些对象时执行的所有代码。然后查看调用该代码的所有代码。这将是大量分析要在“即时”完成。
public class Foo extends Bazz{
    static int count = 0;

    public Foo(){
        // Implicit call to Bazz() has side effect
        count++; // side effect
        Bazz.onNewFoo(this); // reference escaping
    }

    Bazz bazz = new Bazz();  // side effect
    {
        Bazz.onNewBazz(this.bazz); // reference escaping
    }
}

class Bazz{
    static int count = 0;

    static List<Foo> fooList = new LinkedList<>();
    static List<Bazz> bazzList = new LinkedList<>();

    static void onNewFoo(Foo foo){
        fooList.add(foo);
    }

    static void onNewBazz(Bazz bazz){
        bazzList.add(bazz);
    }

    public Bazz(){
        count++;
    }
}

你可能认为我们应该让javac来分析和优化代码。但问题在于,无法保证编译时classpath上的Foo()版本与运行时classpath上的版本相同(这是Java非常有价值的功能之一——它允许我将应用程序从Glassfish迁移到Tomcat而无需重新编译)。因此,我们不能信任编译时进行的分析。
最后,请意识到String与Foo没有区别。我们仍然需要运行分析,而事先无法进行分析(这就是我可以升级JRE而无需重新编译我的应用程序的原因)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接