为什么WeakHashMap在GC之后拥有强大的价值参考?

Rum*_*ter 6 java garbage-collection weakhashmap guava

WeakHashMap中的关键对象变得无法访问.并且应该在GC之后删除映射.但是对价值对象的强烈提及仍然存在.为什么?

使用番石榴弱键图可以观察到相同的行为.

预期产量:

...
refKey.get = null
refValue.get = null
Run Code Online (Sandbox Code Playgroud)

但我得到输出:

map.keys = []
map.values = []
map.size = 0
refKey.get = null
refValue.get = (123)
Run Code Online (Sandbox Code Playgroud)

码:

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());

    }

}
Run Code Online (Sandbox Code Playgroud)

UPD:

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

Fra*_*eau 6

WeakHashMap包含Map.Entry该参考使用密钥实例WeakReference(实际上,在OpenJDK的/ Oracle的JDK,它直接延伸WeakReference).

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

在OpenJDK中,expungeStaleEntries()使用a 时会发生这种情况ReferenceQueue,并且从许多地方调用该方法:

  • size()
  • resize()
  • getTable()它本身是从多种方法调用的,包括get()put()

如果您希望您的值可以被垃圾收集,那么您应该与之交互WeakHashMap,例如通过询问它size()或进行查找.

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

如果我没记错的话,它在番石榴中的工作方式大致相同.