4

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?

yshavit
  • 42,327
  • 7
  • 87
  • 124

2 Answers2

5

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.

rgettman
  • 176,041
  • 30
  • 275
  • 357
  • When I use the code that you demonstrated above with new Object() it does work fine, but what I should do when I want something like Set implementation when key and value have the same reference? – Igal Israilevich Jan 06 '15 at 01:50
  • I'm not aware of any JDK supplied classes that would supply `WeakHashMap`-like behavior for `Set`s. – rgettman Jan 06 '15 at 01:55
  • @IgalIsra Then you probably want the [Guava's Interner](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Interners.html#newWeakInterner()). – maaartinus Jan 06 '15 at 21:33
1

System.gc() is effectively a suggestion to run the garbage collector. There is no guaranteed way to force the garbage collector to run.

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • The [documentation says otherwise](http://docs.oracle.com/javase/7/docs/api/java/lang/System.html#gc()): "When control returns from the method call, the Java Virtual Machine has made a best effort to reclaim space from all discarded objects." – gknicker Jan 06 '15 at 01:41
  • See e.g. http://stackoverflow.com/questions/66540/when-does-system-gc-do-anything , http://stackoverflow.com/questions/2414105/why-is-it-bad-practice-to-call-system-gc, and several other sources. – Louis Wasserman Jan 06 '15 at 02:03
  • More specifically, see http://bugs.java.com/view_bug.do?bug_id=6668279, which is a bug against the documentation. – Louis Wasserman Jan 06 '15 at 02:06
  • Thanks for the clarification and bug reference, I see you are correct. – gknicker Jan 06 '15 at 23:03
  • "best effort" is SLA-speak for "no guarantees whatsoever". – kd8azz Oct 11 '16 at 20:33