Guide to WeakHashMap in Java

1. Overview

In this article, we will be looking at a WeakHashMap from the java.util package.

In order to understand the data structure, we’ll use it here to roll out a simple cache implementation. However, keep in mind that this is meant to understand how the map works, and creating your own cache implementation is almost always a bad idea.

Simply put, the WeakHashMap is a hashtable-based implementation of the Map interface, with keys that are of a WeakReference type.

An entry in a WeakHashMap will automatically be removed when its key is no longer in ordinary use, meaning that there is no single Reference that point to that key. When the garbage collection (GC) process discards a key, its entry is effectively removed from the map, so this class behaves somewhat differently from other Map implementations.

2. Strong, Soft, and Weak References

To understand how the WeakHashMap works, we need to look at a WeakReference class – which is the basic construct for keys in the WeakHashMap implementation. In Java, we have three main types of references, which we’ll explain in the following sections.

2.1. Strong References

The strong reference is the most common type of Reference that we use in our day to day programming:

Integer prime = 1;

The variable prime has a strong reference to an Integer object with value 1. Any object which has a strong reference pointing to it is not eligible for GC.

2.2. Soft References

Simply put, an object that has a SoftReference pointing to it won’t be garbage collected until the JVM absolutely needs memory.

Let’s see how we can create a SoftReference in Java:

Integer prime = 1;  
SoftReference<Integer> soft = new SoftReference<Integer>(prime); 
prime = null;

The prime object has a strong reference pointing to it.

Next, we are wrapping prime strong reference into a soft reference. After making that strong reference null, a prime object is eligible for GC but will be collected only when JVM absolutely needs memory.

2.3. Weak References

The objects that are referenced only by weak references are garbage collected eagerly; the GC won’t wait until it needs memory in that case.

We can create a WeakReference in Java in the following way:

Integer prime = 1;  
WeakReference<Integer> soft = new WeakReference<Integer>(prime); 
prime = null;

When we made a prime reference null, the prime object will be garbage collected in the next GC cycle, as there is no other strong reference pointing to it.

References of a WeakReference type are used as keys in WeakHashMap.

3. WeakHashMap as an Efficient Memory Cache

Let’s say that we want to build a cache that keeps big image objects as values, and image names as keys. We want to pick a proper map implementation for solving that problem.

Using a simple HashMap will not be a good choice because the value objects may occupy a lot of memory. What’s more, they’ll never be reclaimed from the cache by a GC process, even when they are not in use in our application anymore.

Ideally, we want a Map implementation that allows GC to automatically delete unused objects. When a key of a big image object is not in use in our application in any place, that entry will be deleted from memory.

Fortunately, the WeakHashMap has exactly these characteristics. Let’s test our WeakHashMap and see how it behaves:

WeakHashMap<UniqueImageName, BigImage> map = new WeakHashMap<>();
BigImage bigImage = new BigImage("image_id");
UniqueImageName imageName = new UniqueImageName("name_of_big_image");

map.put(imageName, bigImage);
assertTrue(map.containsKey(imageName));

imageName = null;
System.gc();

await().atMost(10, TimeUnit.SECONDS).until(map::isEmpty);

We’re creating a WeakHashMap instance that will store our BigImage objects. We are putting a BigImage object as a value and an imageName object reference as a key. The imageName will be stored in a map as a WeakReference type.

Next, we set the imageName reference to be null, therefore there are no more references pointing to the bigImage object. The default behavior of a WeakHashMap is to reclaim an entry that has no reference to it on next GC, so this entry will be deleted from memory by the next GC process.

We are calling a System.gc() to force the JVM to trigger a GC process. After the GC cycle, our WeakHashMap will be empty:

WeakHashMap<UniqueImageName, BigImage> map = new WeakHashMap<>();
BigImage bigImageFirst = new BigImage("foo");
UniqueImageName imageNameFirst = new UniqueImageName("name_of_big_image");

BigImage bigImageSecond = new BigImage("foo_2");
UniqueImageName imageNameSecond = new UniqueImageName("name_of_big_image_2");

map.put(imageNameFirst, bigImageFirst);
map.put(imageNameSecond, bigImageSecond);
 
assertTrue(map.containsKey(imageNameFirst));
assertTrue(map.containsKey(imageNameSecond));

imageNameFirst = null;
System.gc();

await().atMost(10, TimeUnit.SECONDS)
  .until(() -> map.size() == 1);
await().atMost(10, TimeUnit.SECONDS)
  .until(() -> map.containsKey(imageNameSecond));

Note that only the imageNameFirst reference is set to null. The imageNameSecond reference remains unchanged. After GC is triggered, the map will contain only one entry – imageNameSecond.

4. Conclusion

In this article, we looked at types of references in Java to fully understand how java.util.WeakHashMap works. We created a simple cache that leverages behavior of a WeakHashMap and test if it works as we expected.

The implementation of all these examples and code snippets can be found in the GitHub project – which is a Maven project, so it should be easy to import and run as it is.

Related posts:

Exploring the Spring Boot TestRestTemplate
Handling Errors in Spring WebFlux
Java 8 Stream findFirst() vs. findAny()
Hướng dẫn sử dụng luồng vào ra nhị phân trong Java
Primitive Type Streams in Java 8
Java Program to Implement Levenshtein Distance Computing Algorithm
Queue và PriorityQueue trong Java
Java Program to Check whether Graph is a Bipartite using 2 Color Algorithm
Java Program to Check Multiplicability of Two Matrices
Custom Exception trong Java
An Intro to Spring Cloud Vault
Convert Hex to ASCII in Java
Apache Commons Collections BidiMap
Java Program to Generate a Sequence of N Characters for a Given Specific Case
Java Program to Implement Stack API
Lập trình đa luồng với Callable và Future trong Java
Show Hibernate/JPA SQL Statements from Spring Boot
Java Program to Perform Addition Operation Using Bitwise Operators
Java Program to Represent Graph Using Incidence List
Java Program to Implement Variable length array
Java Program to Implement Hash Trie
Introduction to the Java ArrayDeque
Guide to CountDownLatch in Java
Converting a Stack Trace to a String in Java
Java Program to Generate All Subsets of a Given Set in the Gray Code Order
Java Program to Find Nearest Neighbor Using Linear Search
Java Program to implement Priority Queue
Count Occurrences of a Char in a String
Entity To DTO Conversion for a Spring REST API
Testing an OAuth Secured API with Spring MVC
Java Program to Check if a Point d lies Inside or Outside a Circle Defined by Points a, b, c in a Pl...
Spring Webflux and CORS