java - JVM 垃圾收集对象是否被不再使用的局部变量引用?

标签 java garbage-collection jvm

<分区>

据我所知,方法的局部变量位于执行线程的栈帧中,局部变量的引用类型只有对象的引用,没有对象本身。 JVM 中的所有对象都位于堆空间中。

我想知道正在执行的方法中由局部变量引用的对象在方法执行结束之前永远不会被垃圾回收。 (不使用 java.lang.ref.WeakReference 和 SoftReference。)

它们是垃圾收集的吗?还是从来没有?编译器对这类东西有优化吗?

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

最佳答案

详见 Can java finalize an object when it is still in scope? ,局部变量不会阻止引用对象的垃圾回收。或者,作为 this answer说起来,作用域只是一个语言概念,与垃圾收集器无关。

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

A reachable object is any object that can be accessed in any potential continuing computation from any live thread.

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

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 对象无法访问,因为当包含引用的对象也无法访问时,没有任何后续计算可以访问它。


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

Optimizing transformations of a program can be designed that reduce the number of objects that are reachable to be less than those which would naively be considered reachable. […]

Another example of this occurs if the values in an object's fields are stored in registers. The program may then access the registers instead of the object, and never access the object again. This would imply that the object is garbage.

这不仅仅是一个理论上的选择。正如在 finalize() called on strongly reachable object in Java 8 中讨论的那样,它甚至可能在对象上调用方法时发生,或者换句话说,this 引用可能在实例方法仍在执行时被垃圾回收。

如果终结器也对对象进行同步或调用 Reference.reachabilityFence(object),则唯一可以肯定地防止对象垃圾收集的方法是对对象进行同步。 ,一种在 Java 9 中添加的方法。后来添加的 fence 方法证明了优化器在不同版本之间变得更好对早于预期的垃圾收集问题的影响。当然,首选的解决方案是编写完全不依赖于垃圾回收时间的代码。

关于java - JVM 垃圾收集对象是否被不再使用的局部变量引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54937469/

相关文章:

Java GC 不明确的日志消息

java - scalac v javac 和 scala v java

java - 如何获取 session token 表单 url?

java - 带有 actionbarsherlock 的 Android ICS Spinner

java - 计算排序列表中单词的频率

java - 获取 Android 通知以显示为横幅

Java - 可用的垃圾收集算法

performance - Java 7 与 Java 5 垃圾收集

binding - Clojure 绑定(bind)不起作用

java - Java 运行时注解在内部是如何工作的?