javagarbage-collectionweak-referencesweakhashmap

Java WeakHashMap Class


I wanted to test Java WeakHashMap Class functionality and for that matter I wrote the following test:

public class WeakHashMapTest {

public static void main(String args[]) {
    Map<String, Object> weakMap = new WeakHashMap<>();
    String x = new String("x");     
    String x1 = new String("x1");   
    String x2 = new String("x2");   
    weakMap.put(x, x);
    weakMap.put(x1, x1);
    weakMap.put(x2, x2);
    System.out.println("Map size :" + weakMap.size());
    // force all keys be eligible
    x=x1=x2=null;
    // call garbage collector 
    System.gc();
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    System.out.println("Map size :" + weakMap.size());
    System.out.println("Map :" + weakMap);
}

}    

After running the class WeakMapTest I was unpleasantly surprised to get the following output:

map before gc: {x=x, x1=x1, x2=x2} map after gc: {x=x, x1=x1, x2=x2}

when I expected that the map will be empty.

That is, the garbage collector didn't do its work. But why?


Solution

  • The WeakHashMap will have its keys reclaimed by the garbage collector when they are no longer strongly reachable.

    Implementation note: The value objects in a WeakHashMap are held by ordinary strong references. Thus care should be taken to ensure that value objects do not strongly refer to their own keys, either directly or indirectly, since that will prevent the keys from being discarded.

    However, because you used the key itself as the value, the value is still strongly reachable, which means that the garbage collector can't reclaim the keys.

    However, if you use other objects as values, then the only references to the keys will be the keys themselves.

    weakMap.put(x, new Object());
    weakMap.put(x1, new Object());
    weakMap.put(x2, new Object());
    

    Then, after clearing the variables and calling the garbage collector as you've done already, I get the output:

    Map size :3
    Map size :0
    Map :{}
    

    Even if the call to System.gc() doesn't guarantee the garbage collector runs, it looks like it does run here.