Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why WeakHashMap holds strong reference to value after GC?

Key object in WeakHashMap became weakly reachable. And map should be remove the entry after GC. But a strong reference to the value object remains. Why?

The same behavior is observed with guava weakkeys map.

Expected output:

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

But I get output:

map.keys = []
map.values = []
map.size = 0
refKey.get = null
refValue.get = (123)

Code:

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:

I tried perform GC in jСonsole and jcmd but output was not changed.

like image 801
Rumter Avatar asked Jan 19 '16 13:01

Rumter


People also ask

What is true about WeakHashMap garbage collector?

WeakHashMap is an implementation of the Map interface that stores only weak references to its keys. Storing only weak references allows a key-value pair to be garbage-collected when its key is no longer referenced outside of the WeakHashMap.

Where do I use WeakHashMap?

If you have some objects that are reused often in your application, and their construction is expensive, and there are too many of them to keep them all in memory - - you use WeakHashMap. Put there the object which is not currently used. When this object is needed - get it out of the map.


1 Answers

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.

like image 146
Frank Pavageau Avatar answered Oct 13 '22 01:10

Frank Pavageau