Search code examples
javagarbage-collectionweak-referencessoft-references

Map Size incorrect in Java. If I wrap Key and Value in WeakReference and then add into HashMap, printed size is different than expected


If I don't comment line 1 and comment line 2, line 1 causes OutOfMemoryError. If I do the reverse, it does not causes OutOfMemoryError because <Key,Value> are wrapped in WeakReference. But i can't understand the output of line 3 ---- i : 3869 Size : 3870 maxSize : 3870.

From Java Docs :

Because the garbage collector may discard keys at any time, a WeakHashMap may behave as though an unknown thread is silently removing entries.

Based on this statement size should reduce but line 3 output seems continuously increasing. Why so ?

import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;

public class WrongWeakHashMapSize {

private static Map map = new HashMap();

public static void main(String[] args) {
    for (int i = 0, maxSize = 0; i < 10000000L; ++i) {
        /*
         * Line 1 Causes java.lang.OutOfMemoryError: Java heap space Error.
         * After this line i : 244 Size : 490 maxSize : 490.
         */
        map.put(new LargeObject(i), new Integer(i)); // Line 1
        /* After Commenting Line 1 :---- 
         * Line 2 Does not Cause java.lang.OutOfMemoryError. Because of WeakReference class use.I think Line 3
         * is showing wrong Size of MAP. it printed
         * i : 3869 Size : 3870 maxSize : 3870 which seems almost impossible
         * because 3870 objects of LargeObject can not be exist at a time.
         */
        map.put(new WeakReference(new LargeObject(i)), new WeakReference(new Integer(i))); // Line 2
        maxSize = maxSize < map.size() ? map.size() : maxSize; // Line 3
        System.out.println("i : " + i + " Size : " + map.size() + " maxSize : " + maxSize); // Line 4
    }
}

public static class LargeObject {
    private final byte[] space = new byte[1024 * 1024];
    private final int id;

    public LargeObject(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }
}
}

Solution

  • That's because map.size() gives you the number of key-value pairs in the map. Garbage collection of weak references will not remove the reference from the map, it will only garbage the object.

    The only effect of the WeakReference will be that some of the LargeObjects and Integers will be eligible for discard, the key-value mapping is still in the map and so is still counted.