弱引用对象不会被垃圾收集

Weakly referenced object won't get garbage collected

我担心的是某个对象的实例曾经被强引用,但是在对其强引用进行显式空赋值和显式 System.gc() 调用之后,该实例仍然可以通过 弱引用。如果我理解正确的话,当被引用的对象只剩下弱引用时,保证在下一个 GC 会话中清除引用对象。我错过了什么?

参考代码:

public class References {
    public static void main(String[] args) {

        Example strongReferenceWrappedInWeak = new Example(42);
        strongReferenceWrappedInWeak.printA();

        WeakReference<Example> exampleWeakReference = new WeakReference<>(strongReferenceWrappedInWeak);

        System.gc();

        Example retrievedExample = exampleWeakReference.get();
        retrievedExample.printA(); //this works, because a strong reference is present to the instance, hence it's not cleared

        strongReferenceWrappedInWeak = null; //eligible for garbage collection

        System.gc();

        Example retrievedExampleTwo = exampleWeakReference.get(); //should be null
        retrievedExampleTwo.printA(); //should throw NPE
    }
}

class Example {
    private int a;

    Example(int a) {
        this.a = a;
    }

    void printA() {
        System.out.println(this.a);
    }
}

垃圾收集以神秘的方式工作。

Java 生态系统中有多种垃圾收集器的实现,它们的行为截然不同。

何时进行垃圾收集 运行s 因垃圾收集器的实现而异,也可能取决于 JVM 的当前状况。一个收集器可能 运行 几乎持续不断,而另一个收集器可能会一直等到内存不足。 (为了表达清楚,我在这里过于简单化了。)

是收集所有垃圾,还是只收集其中的一部分,也可能因收集器实现和 JVM 的状态而异。

调用System.gc只是一个建议,不是命令。垃圾收集器可以随意忽略它。

在 Java 中,您不应该在管理内存方面花费太多精力。现代 JVM 实现在这方面比任何单个程序员都可能做得更好。只需确保在使用完对象后释放对对象的所有引用。或者使用 WeakReference/SoftReference。然后信任 JVM 和垃圾收集器来完成它的工作。

在极端情况下(非常大的内存,或大量的对象流失),您可能想要研究各种垃圾收集器实现的行为。也许可以考虑替代方案,例如来自 Azul Systems 的 Zing 或来自 Oracle 的 GraalVM。但对于大多数项目,通常基于 OpenJDK 的 JVM 工作得很好。

strongReferenceWrappedInWeak = null 不会使 Example 对象实例符合垃圾回收条件,因为 retrievedExample 仍然保持对它的强引用

要修复,请添加 retrievedExample = null;

Example strongReferenceWrappedInWeak = new Example(42);
strongReferenceWrappedInWeak.printA();

WeakReference<Example> exampleWeakReference = new WeakReference<>(strongReferenceWrappedInWeak);

System.gc();

Example retrievedExample = exampleWeakReference.get();
retrievedExample.printA(); //this works, because a strong reference is present to the instance, hence it's not cleared

retrievedExample = null;
strongReferenceWrappedInWeak = null; //now eligible for garbage collection

System.gc();

Example retrievedExampleTwo = exampleWeakReference.get(); //will be null
retrievedExampleTwo.printA(); //will throw NPE

或者,不要使用局部变量创建强引用,直接从弱引用调用方法即可。这样你就不会像以前那样不小心留下强引用。 *(在 printA() 调用期间,this 引用是强引用,因此在 调用期间 不能对对象进行 GC)*

Example strongReferenceWrappedInWeak = new Example(42);
strongReferenceWrappedInWeak.printA();

WeakReference<Example> exampleWeakReference = new WeakReference<>(strongReferenceWrappedInWeak);

System.gc(); //does not collect object, since strong reference still exists

exampleWeakReference.get().printA(); //works

strongReferenceWrappedInWeak = null; //eligible for garbage collection
System.gc(); //collects object, since it is now weakly referenced only

exampleWeakReference.get().printA(); //throws NPE

输出(来自两者)

42
42
Exception in thread "main" java.lang.NullPointerException
    at Test.main(Test.java:**)

测试于 Java 13