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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With