Java docs(jdk1.8.0_71) says that,
an entry in a WeakHashMap
will automatically be removed if it's key
is no longer in ordinary use. More precisely, the presence of a
mapping for a given key will not prevent the key from being discarded
by the garbage collector.
This clearly states that when the key
has been discarded, its Entry<K, V>
will also be removed from the Map
.
Now, lets take a look under the hood and check what happens when the key
is removed.
The code snippet of remove(Object o)
method is as shown below:
public V remove(Object key) {
Object k = maskNull(key);
int h = hash(k);
Entry<K,V>[] tab = getTable();
int i = indexFor(h, tab.length);
Entry<K,V> prev = tab[i];
Entry<K,V> e = prev;
while (e != null) {
Entry<K,V> next = e.next;
if (h == e.hash && eq(k, e.get())) {
modCount++;
size--;
if (prev == e)
tab[i] = next;
else
prev.next = next;
return e.value;
}
prev = e;
e = next;
}
return null;
}
As it can be observed, the method first fetches the array of Entry<K, V>
by calling getTable()
method. On every call to getTable()
, another method expungeStaleEntries()
is called. This particular method will play around with a ReferenceQueue<Object>
that holds cleared WeakEntries
and it removes the stale entries from the Entry<K, V>[] table
. The code snippet of this method can be seen below:
/**
* Expunges stale entries from the table.
*/
private void expungeStaleEntries() {
for (Object x; (x = queue.poll()) != null; ) {
synchronized (queue) {
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>) x;
int i = indexFor(e.hash, table.length);
Entry<K,V> prev = table[i];
Entry<K,V> p = prev;
while (p != null) {
Entry<K,V> next = p.next;
if (p == e) {
if (prev == e)
table[i] = next;
else
prev.next = next;
// Must not null out e.next;
// stale entries may be in use by a HashIterator
e.value = null; // Help GC
size--;
break;
}
prev = p;
p = next;
}
}
}
}
As it can be seen through the code snippet that, each Entry being added to a WeakHashMap
, stores that entry in this queue
(The calling constructor during a put(K, V)
operation is as shown below:
Entry<K,V> e = tab[i];
tab[i] = new Entry<>(k, value, queue, h, e);
) and that same entry is retrieved and removed from the queue
during expunge operation. The value of this removed Entry<K, V>
is set to null e.value = null
, which will be GC'd later. And yes, it doesn't have any control on GC. This is how WeakHashMap
facilitates discarding of mapped values with keys.