WeakHashMap stores key value pairs just like HashMap, however, the keys in WeakHashMaps are stored indirectly as a Weak Reference. This causes the entries in WeakHashMap get automatically removed when it's key does not have any strong reference. Well, you might not call it automatic, because there is a lot of work that goes into before the entries are removed. We will see how in just a moment. But, before that, go through my HashMap code walkthrough before starting this section, as it is absolutely required to understand WeakHashMap.
https://delveintojava.blogspot.com/2019/11/hashmap-walkthrough.html
Let's first understand what a WeakReference is.
Unlike Strong references, Weak reference allows the object that it references get garbage collected.
Below is an example for WeakReference.
// val is strong to string object
// Creating weakReference to string object
Now that we are clear about WeakReference, let's talk WeakHashMap. Let's start with an example.
WeakHashMap<String, String> weakHashMap = new WeakHashMap<>();
// strong Removing reference to key1
Can you guess the output?
OUTPUT:
{key1=value1, key2=value2} // Before GC
{key2=value2} // After GC (Note that System.gc() might not cause GC immediately, so you might get both key value pairs second time also)
As you can see, after garbage collection, the entry with key1 and value1 are removed since the strong reference for key1 was removed by making it null. So, the key string had only weak reference to it and was eligible for garbage collection.
Had it been a HashMap, the output would be as below:
{key1=value1, key2=value2} // Before GC
{key2=value2, key2=value2} // After GC
As you can see, all values are retained even after GC.
So, how does WeakhashMap know that the key has been garbage collected ? That information will be required to remove the weakReference node, don't you think ?
The answer is ReferenceQueue. Before we talk about ReferenceQueue let's see the the blueprint of WeakHashmap's entries.
You can see that here, entry does not extend the regular HashMap node. The entry extends WeakReference using reference field as key since we are passing key to the super class constructor, and not the whole object(this reference).
We can also see, a reference queue has been passed to the super constructor. To understand how ReferenceQueue plays part in WeakHashMap implementation, let's understand it first.
It's all garbage collection story. Let's rewind a little, and see what a garbage collector does when it comes across an object that is weakly reachable i.e an object which has only weak reference to it.
https://delveintojava.blogspot.com/2019/11/hashmap-walkthrough.html
Let's first understand what a WeakReference is.
Unlike Strong references, Weak reference allows the object that it references get garbage collected.
Below is an example for WeakReference.
// val is strong to string object
String val = new String("Hello");
// Creating weakReference to string object
WeakReference<String> weakReference = new WeakReference<String>(val);
// This if condition will always hold true, as string still has a strong reference.
if (null != weakReference.get()) {
System.out.println(weakReference.get());
}
// Remove strong reference to string object
val = null;
// Invoke system garbage collection. This might not result in immediate GC
System.gc();
// At this point the object could be garbage collected as it has only weak reference
if (null != weakReference.get()) {
System.out.println(weakReference.get());
} else {
System.out.println("Garbage collection cleared weak reference");
}
Now that we are clear about WeakReference, let's talk WeakHashMap. Let's start with an example.
WeakHashMap<String, String> weakHashMap = new WeakHashMap<>();
// Create strong references for both key and values
String key1 = new String("key1");
String key2 = new String("key2");
String value1 = new String("val1");
String value2 = new String("val2");
weakHashMap.put(key1, value1);
weakHashMap.put(key2, value2);
System.out.println(weakHashMap);
// strong Removing reference to key1
key1 = null;
System.gc();
System.out.println(weakHashMap);
Can you guess the output?
OUTPUT:
{key1=value1, key2=value2} // Before GC
{key2=value2} // After GC (Note that System.gc() might not cause GC immediately, so you might get both key value pairs second time also)
As you can see, after garbage collection, the entry with key1 and value1 are removed since the strong reference for key1 was removed by making it null. So, the key string had only weak reference to it and was eligible for garbage collection.
Had it been a HashMap, the output would be as below:
{key1=value1, key2=value2} // Before GC
{key2=value2, key2=value2} // After GC
As you can see, all values are retained even after GC.
So, how does WeakhashMap know that the key has been garbage collected ? That information will be required to remove the weakReference node, don't you think ?
The answer is ReferenceQueue. Before we talk about ReferenceQueue let's see the the blueprint of WeakHashmap's entries.
You can see that here, entry does not extend the regular HashMap node. The entry extends WeakReference using reference field as key since we are passing key to the super class constructor, and not the whole object(this reference).
We can also see, a reference queue has been passed to the super constructor. To understand how ReferenceQueue plays part in WeakHashMap implementation, let's understand it first.
It's all garbage collection story. Let's rewind a little, and see what a garbage collector does when it comes across an object that is weakly reachable i.e an object which has only weak reference to it.
- First, the WeakReference object's referent field is set to null. So, now WeakReference doesn't refer to any object in the heap.
- Then the WeakReference object is added to the ReferenceQueue, and the object's memory in the heap is freed.
Whenever JVM collects the WeakReference's key, the entry (which is a weak reference object) is added to the ReferenceQueue that we pass during the entry creation. So, WeakHashMap checks this reference queue and if it finds any weak reference objects in the queue, it removes the same from it's own table/bucket as well.
This all happens in the expungeStaleEntries function. expungeStaleEntries keeps an eye on the queue and updates its internal table accordingly.
queue is a private final member variable of WeakHashMap.
expungeStaleEntries function is called from multiple places within the WeakHashMap implementation. For example, re-sizing, calculating size, get value etc.
As you can see, we are polling the reference queue. As long as it keeps finding WeakReference entry objects in the reference queue, it finds the bucket index where the object is present. Then it moves through the bucket to find the specific entry. Once the entry in the bucket matches (Notice that the entries are compared through reference (address) comparison (p == e)) the entry present in reference queue, it removes that entry from the bucket.
At last, let's revise the difference between the HashMap and WeakHashMap.
References:
No comments:
Post a Comment