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

2022-09-02 13:57:27

在下面的示例中,有两种功能等效的方法:

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;
    }
}

然而,在它们的first()中,字符串“s1”显然可用于语句之前的垃圾回收。在第二()字符串中,“s1”仍然是可访问的(尽管从代码审查的角度来看,它不再使用)。method1returnmethod2

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

编辑:有时,变量可以引用对象,如完全渲染的图像,并对内存产生影响。

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

我真的更喜欢不进行重新分配的代码,因为它更容易阅读和推理。

更新:根据 jls-12.6.1

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

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


答案 1

不,因为你的代码可以想象地检索它并用它做一些事情,而抽象的JVM不考虑什么代码即将到来。但是,一个非常、非常、非常聪明的优化JVM可能会提前分析代码,发现没有办法被引用,并对其进行垃圾回收。不过,你绝对不能指望这一点。s1


答案 2

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

然而,在JITC的情况下,即使是温和的优化也可能认识到S1是未使用的,并回收注册S2。或者可能不是。GC 将检查寄存器内容,如果 S1 已重新用于其他用途,则将回收旧的 S1 对象(如果未以其他方式引用)。如果尚未重用 S1 位置,则可能无法回收 S1 对象。

“可能不会”是因为,根据 JVM,JITC 可能会或可能不会向 GC 提供对象引用在程序流中“实时”位置的映射。如果提供此地图,则可能会或可能不会精确地识别S1的“实时范围”(最后一个参考点)的终点。许多不同的可能性。

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