当堆栈中仍然可见时,未使用的对象是否可用于垃圾收集?

时间:2013-11-11 00:04:26

标签: java garbage-collection jvm

在以下示例中,有两个功能相同的方法:

public class Question {

    public static String method1() {
        String s = new String("s1");
        // some operations on s1
        s = new String("s2");
        return s;
    }

    public static String method2() {
        final String s1 = new String("s1");
        // some operations on s1
        final String s2 = new String("s2");
        return s2;
    }
}

但是在它们的第一个(method1)中,字符串“s1”在return语句之前显然可用于垃圾收集。在第二个(method2)字符串“s1”仍然可以访问(虽然从代码审查预期它不再使用)。

我的问题是 - 在jvm规范中是否有任何内容表明,一旦变量在堆栈中未使用,它就可用于垃圾收集?

修改 的 有时变量可以像完全渲染的图像一样引用对象,并且会对内存产生影响。

我是因为实际考虑而问的。我在一个方法中有大量内存贪婪的代码,并且只是通过将这个方法分成几个小方法来思考是否可以帮助JVM(一点点)。

我更喜欢没有重新分配的代码,因为它更容易阅读和推理。

更新 :每jls-12.6.1

  

Java编译器或代码生成器可以选择设置一个不再用于null的变量或参数,以使此类对象的存储可能更快地回收

因此看起来GC可以声明仍然可见的对象。我怀疑,然而这种优化是在离线编译期间完成的(它会搞砸调试),很可能是由JIT完成的。

5 个答案:

答案 0 :(得分:4)

不,因为您的代码可以想象地检索它并对其执行某些操作,并且抽象JVM不会考虑将要使用的代码。但是,一个非常非常非常聪明的优化JVM可能会分析前面的代码并发现无法引用s1,并且垃圾收集它。但你绝对不能指望这一点。

答案 1 :(得分:4)

如果您正在谈论解释器,那么在第二种情况下,S1保持“引用”,直到该方法退出并且堆栈帧被卷起。 (也就是说,在标准解释器中 - GC完全有可能使用来自方法验证的活跃信息。而且,此外(并且更有可能),javac可以进行自己的活跃度分析并基于此分享“共享”解释器插槽。 )

然而,在JITC的情况下,即使是稍微优化的人也可能认识到S1未使用并且循环注册S2。或者它可能不会。 GC将检查寄存器内容,如果S1已被重用于其他内容,则将回收旧的S1对象(如果没有另外引用)。如果尚未重用S1位置,则可能不会回收S1对象

“可能不会”,因为根据JVM,JITC可能会也可能不会向GC提供对象引用在程序流中“活动”的位置的映射。并且该地图(如果提供)可以或可以不精确地识别S1的“有效范围”(最后一个参考点)的结束。许多不同的可能性。

请注意,这种潜在的可变性不违反任何Java原则 - GC不需要尽早回收对象,并且没有实际的方法让程序对回收对象时精确敏感。 / p>

答案 2 :(得分:1)

VM可以在方法退出之前自由优化代码以使s1无效(只要它是正确的),因此s1可能更早符合垃圾条件。

然而,这几乎是不必要的。许多方法调用必须在下一个GC之前发生;无论如何,所有堆栈帧都已被清除,无需担心特定方法调用中的特定局部变量。

就Java语言而言,垃圾可以永久存在而不会影响程序语义。这就是JLS几乎没有谈论垃圾的原因。

答案 3 :(得分:0)

  

在其中第一个字符串“s1”显然可用于返回语句之前的垃圾收集

根本不清楚。我认为你将“未使用”与“无法访问”混为一谈。他们不一定同样的事情。

从形式上讲,变量是有效的,直到其封闭范围终止,因此在此之前它不可用于垃圾收集。

然而,“Java编译器或代码生成器可能会选择设置一个不再用于null的变量或参数,以使此类对象的存储可能更快地回收”JLS #12.6.1

答案 4 :(得分:0)

基本上堆栈帧和静态区域被GC视为根。因此,如果从任何堆栈帧引用对象,则认为它是活动的。从活动堆栈帧中回收一些对象的问题是GC与应用程序(mutator)并行工作。您如何看待GC在方法进行过程中发现该对象未被使用?这将需要一个非常繁重和复杂的同步,事实上这将打破GC与mutator并行工作的想法。每个线程都可以将变量保存在处理器寄存要实现您的逻辑,还应将它们添加到GC根目录中。我甚至无法想象如何实现它。

回答你的问题。如果您有任何逻辑产生许多将来未使用的对象,请将其分离为不同的方法。这实际上是一种很好的做法。

您还应该通过JVM进行int帐户优化(如EJP指出)。还有一个转义分析,可能会阻止对象进行堆分配。但依靠你的代码表现是一种不好的做法