Java 中的 WeakReference 和 StrongReference 的 GC

GC for a WeakReference and StrongReference in Java

我在 java api 文档中阅读了一些关于 Java 7 的弱引用的文献,并尝试使用以下内容进行测试

    package com.finalize;

import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;

public class WeakRefernceTest {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String x = new String("x1");
        String y = new String("x2");

        Map<String, Object> mw = new WeakHashMap<>();
        mw.put(x, new Object());

        Map<String, Object> m = new HashMap<>();
        m.put(y, new Object());

        x = null;
        y = null;

        for(int i = 0; i < 5;++i)
        System.gc();

        System.out.println(mw.size());
        System.out.println(m.size());

    }

}

当我多次 运行 时,我可以看到 mw.size() 打印为 0。但我有点困惑,因为 m.size() 从未打印为0. 所以基本上,我试图了解这个热点 jvm 中 运行 的默认 GC。那么当GC开始标记可达对象时,x和y设置为null后运行,为什么因为y已经设置为null而将y所指的对象标记为可达对象呢?即使根变量设置为 m,为什么 GC 会到达 y 之前指向的对象并声明它是可达的?

相比之下,x 上生命周期变化的详细顺序是什么?

单步执行您的代码

  • 您复制字符串 "x2" 并将其分配给 y
  • 您将字符串 "x2" 添加为 m
  • 中的键
  • 当您进行 GC 时,m 是可访问的,它的所有键都是可访问的。
  • 在 GC 之后,m 及其所有键都被保留。

您正在主线程上执行 main 方法。主线程的堆栈上有变量 m。此变量 m 是对 HashMap 实例的引用。此 HashMap 实例具有对映射条目数组的引用。此数组中包含的唯一映射条目具有对 "x2" 键和相应对象的引用。

因此存在从根(主线程堆栈)到映射中存储的对象的强大引用链。这会阻止 GC 收集映射中的对象。

thread stack -> m -> HashMap -> entries -> entry -> "x2"
                                                 -> Object

另一个映射和另一个对象也是如此,除了它是一个 WeakHashMap,所以因为映射键是一个弱引用并且是唯一指向的引用"x1",允许 GC 收集对 "x1" 的引用。一旦 GC 收集了它,地图就会从其数组中删除相应的条目。