Search code examples
javaconcurrencyweak-references

ConcurrentWeakKeyHashMap size() method


Trying to make sense of how ConcurrentWeakKeyHashMap responds to memory condition/garbage collection.

With the code below in my JUnit test with maxNum == 6000, the assert statements failed, with size == 4123 (or something similar).

I set my JVM -Xmx to 500m, with the hope of getting 6000, but no luck.

Assuming the size changes because of Garbage Collection, on what condition does the weak key get reclaimed? i.e. "low memory, low something else" condition ?

    int maxNum = 6000;
    int initalCapacity = 2*maxNum;
    ConcurrentMap<String,String> concurrentMap = new ConcurrentWeakKeyHashMap<String,String>(initalCapacity);       

    int count = 0;
    for( int i=0; i<maxNum; i++) {
        String key = "k" + i;
        String value = "v" + i;
        concurrentMap.put(key, value);
        count = i;
        //System.out.println(concurrentMap.size());
    }

    int size = concurrentMap.size();
    assertEquals(size, maxNum);
    System.gc();
    size = concurrentMap.size();
    assertEquals(size, maxNum);

EDIT

By pinning the weak key in a strong object, I always get 6000/maxNumber. i.e.

    // our strong object 
    List<String> strongList = new ArrayList();
    for( int i=0; i<maxNum; i++) {
        String key = "k" + i;
        String value = "v" + i;
        concurrentMap.put(key, value);

            // key is now pinned in strong object
        strongList.add(key);
    }

    // size will now equal to maxNum, as nothing gets reclaimed
    int size = concurrentMap.size();

Solution

  • A weak reference gets reclaimed when there is no longer any "strong" reference to the object.

    This happens due to garbage collection, which in turn happens rather unpredictably in the background (or when you run out of memory). In particular it can also happen even when there still is plenty of memory available (especially for objects that "died young", the system tries to collect those early).

    I would also not assume that ConcurrentWeakHashMap#size is completely accurate all the time. It has a method purgeStaleEntries that you should probably call to get more accurate.