JVM垃圾收集不再使用的本地变量所引用的对象吗?



据我所知,方法的本地变量位于执行线程中的堆栈框架中,而本地变量的参考类型仅具有对象的参考,而不是对象本身。JVM中的所有对象都位于堆空间中。

我想知道,在执行方法执行结束之前,在执行的方法中引用的本地变量所引用的对象永远不会收集。(不使用java.lang.ref.weakreference和softreference。)

他们收集了垃圾吗?还是从来没有?是否有编译器对此类内容的优化?

(如果从未收集过垃圾,这意味着可能需要将null分配给不再使用的变量,而执行大型方法则需要很长时间。)

在Java中详细说明对象仍可以在范围内完成?或者,正如此答案所说的那样,范围是一种语言概念,与垃圾收集器无关。

我将再次引用规范的相关部分,JLS§12.6.1:

a 访问对象是可以从任何潜在的持续计算中访问的任何对象。

此外,我将答案的示例扩展到

class A {
    static volatile boolean finalized;
    Object b = new Object() {
        @Override protected void finalize() {
            System.out.println(this + " was finalized!");
            finalized = true;
        }
        @Override public String toString() {
            return  "B@"+Integer.toHexString(hashCode());
        }
    };
    @Override protected void finalize() {
        System.out.println(this + " was finalized!");
    }
    @Override public String toString() {
        return super.toString() + " with "+b;
    }
    public static void main(String[] args) {
        A a = new A();
        System.out.println("Created " + a);
        for(int i = 0; !finalized; i++) {
            if (i % 1_000_000 == 0)
                System.gc();
        }
        System.out.println("finalized");
    }
}
Created A@59a6e353 with B@6aaa5eb0
B@6aaa5eb0 was finalized!
finalized
A@59a6e353 with B@6aaa5eb0 was finalized!

表明,即使在范围中具有变量的方法也可以检测到引用对象的最终确定。此外,从堆变量引用也不一定会阻止垃圾收集,因为B对象是无法实现的,因为当包含参考的对象也无法实现时,没有继续计算可以访问它。


值得强调的是,即使使用该对象并不总是阻止其垃圾收集。重要的是,是否需要对对象的内存进行持续的操作,而不是源代码中对象字段的每个访问都必须导致运行时的实际内存访问。规范指出:

可以设计

优化程序的转换,以减少可将到达的对象数量降低,而这些对象的数量比可天真地认为可以达到的对象的数量。[…]

如果对象字段中的值存储在寄存器中,则会发生另一个示例。然后,程序可以访问寄存器而不是对象,而切勿再次访问对象。这意味着该物体是垃圾。

这不仅是理论选择。正如finalize()在Java 8中的强触及对象中所讨论的那样,当对象在其上调用方法时,它甚至可能发生在对象上,换句话说,this参考可能会在实例方法仍在执行时收集垃圾。<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

肯定可以防止对象垃圾收集的唯一方法是在对象上同步,如果最终确定器也确实在对象上同步或调用Reference.reachabilityFence(object),则在Java 9中添加了一种方法。在更早的垃圾收藏问题上,优化器的优化器在版本中变得更好。当然,首选的解决方案是编写完全不取决于垃圾收集时间的代码。

所有对象都在堆中并不是真的。但这通常是正确的。Java已扩展到具有堆栈 - 本地对象,前提是JVM可以检测到该对象只能与堆栈框架保持生存。

现在,对于堆上的对象,该对象在方法中具有本地参考。在处理该方法时,与该方法运行相关的堆栈框架包含局部变量引用。只要可以使用参考(包括仍在堆栈框架中),就不会收集对象。

一旦引用被销毁,并且无法通过运行程序来达到对象(因为没有参考可以到达它),则垃圾收集器将收集。

最新更新