Why WeakHashMap holds strong reference to value after GC?

心已入冬 提交于 2019-11-30 14:20:06

The WeakHashMap contains Map.Entry instances which reference the key using a WeakReference (actually, in OpenJDK / Oracle JDK, it directly extends WeakReference).

When the GC happens, the entries which now reference absent keys are not magically removed from the map: they're still present until cleared, which is why the value is also still present and has not been collected yet.

In OpenJDK, that happens in expungeStaleEntries() using a ReferenceQueue, and that method is called from a number of places:

  • size()
  • resize()
  • getTable() which is itself called from multiple methods, including get() and put()

If you want your value to be garbage collectable, you should interact with the WeakHashMap, e.g. by asking for its size() or doing a lookup.

Note that it means the value cannot be collected until a second garbage collection.

If I remember correctly, it works more or less the same way in Guava.

Running this modification of the code and forcing GC in Jconsole confirms removal of the reference.

    System.gc();

    try {
        while (refValue.get() != null) {
            System.out.println("map.keys = " + map.keySet());
            Thread.sleep(5000);
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

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

For some reason though, if map.keySet() is not queried in the loop, it never terminates, that is refValue does not become null.

You have no control over GC in java. the VM decides. I've never run across a case where System.gc() is needed. Since a System.gc() call simply SUGGESTS that the VM do a garbage collection and it also does a FULL garbage collection (old and new generations in a multi-generational heap), then it can actually cause MORE cpu cycles to be consumed than necessary.

In jdk 1.7 onwards you can use below command to force gc to run

jcmd <pid> GC.run

Get processid using jps

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!