WeakHashMap 中的关键对象变得弱可达。并且 map 应该在 GC 之后删除条目。但是,仍然存在对值(value)对象的强烈引用。为什么?

用 Guava 弱键映射观察到相同的行为。

预期输出:

...
refKey.get = null
refValue.get = null

但我得到输出:
map.keys = []
map.values = []
map.size = 0
refKey.get = null
refValue.get = (123)

代码:
import java.lang.ref.WeakReference;
import java.util.Map;
import java.util.WeakHashMap;
import com.google.common.collect.MapMaker;

public class Test {

    static class Number {
        final int number;
        public Number(int number) { this.number = number; }
        public String toString() { return "(" + number + ")"; }
    }

    static class Key extends Number {
        public Key(int number) { super(number); }
    }

    static class Value extends Number {
        public Value(int number) { super(number); }
    }

    public static void main(String args[]) {

        //Map<Key, Value> map = new MapMaker().weakKeys().makeMap();
        Map<Key, Value> map = new WeakHashMap<>();

        Key key = new Key(1);
        Value value = new Value(123);

        map.put(key, value);

        WeakReference<Key> refKey = new WeakReference<>(key);
        WeakReference<Value> refValue = new WeakReference<>(value);

        key = null;
        value = null;

        System.gc();

        System.out.println("map.keys = " + map.keySet());
        System.out.println("map.values = " + map.values());
        System.out.println("map.size = " + map.size());
        System.out.println("refKey.get = " + refKey.get());
        System.out.println("refValue.get = " + refValue.get());

    }

}

UPD:

我尝试在 jСonsole 和 jcmd 中执行 GC 但输出没有改变。

最佳答案

WeakHashMap 包含 Map.Entry 实例,这些实例使用 WeakReference 引用 key (实际上,在 OpenJDK/Oracle JDK 中, it directly extends WeakReference )。

当 GC 发生时,现在引用不存在的键的条目不会从映射中神奇地删除:它们在清除之前仍然存在,这就是为什么该值仍然存在并且尚未被收集的原因。

在 OpenJDK 中,这在 expungeStaleEntries() 中使用 ReferenceQueue 发生,并且该方法从多个地方调用:

  • size()
  • resize()
  • getTable() 本身被多种方法调用,包括 get()put()

  • 如果您希望您的值可以被垃圾回收,您应该与 WeakHashMap 进行交互,例如通过询问其 size() 或进行查找。

    请注意,这意味着在 第二次 垃圾收集之前无法收集该值。

    如果我没记错的话,它在 Guava 中的工作方式或多或少相同。

    10-07 12:24