Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OutOfMemoryErrors even after using WeakReference's for keys and values

Tags:

java

Below is a small test I've coded to educate myself on references API. I thought this would never throw OOME but it is throwing it. I am unable to figure out why. appreciate any help on this.

public static void main(String[] args)
{
    Map<WeakReference<Long>, WeakReference<Double>> weak = new HashMap<WeakReference<Long>, WeakReference<Double>>(500000, 1.0f);

    ReferenceQueue<Long> keyRefQ = new ReferenceQueue<Long>();
    ReferenceQueue<Double> valueRefQ = new ReferenceQueue<Double>();

    int totalClearedKeys = 0;
    int totalClearedValues = 0;

    for (long putCount = 0; putCount <= Long.MAX_VALUE; putCount += 100000)
    {
        weak(weak, keyRefQ, valueRefQ, 100000);

        totalClearedKeys += poll(keyRefQ);
        totalClearedValues += poll(valueRefQ);

        System.out.println("Total PUTs so far    = " + putCount);
        System.out.println("Total KEYs CLEARED so far    = " + totalClearedKeys);
        System.out.println("Total VALUESs CLEARED so far = " + totalClearedValues);
    }

}

public static void weak(Map<WeakReference<Long>, WeakReference<Double>> m, ReferenceQueue<Long> keyRefQ,
        ReferenceQueue<Double> valueRefQ, long limit)
{
    for (long i = 1; i <= limit; i++)
    {
        m.put(new WeakReference<Long>(new Long(i), keyRefQ), new WeakReference<Double>(new Double(i), valueRefQ));
        long heapFreeSize = Runtime.getRuntime().freeMemory();
        if (i % 100000 == 0)
        {
            System.out.println(i);
            System.out.println(heapFreeSize / 131072 + "MB");
            System.out.println();
        }

    }
}

private static int poll(ReferenceQueue<?> keyRefQ)
{
    Reference<?> poll = keyRefQ.poll();
    int i = 0;
    while (poll != null)
    {
        // 
        poll.clear();
        poll = keyRefQ.poll();
        i++;
    }
    return i;

}

}

And below is the log when ran with 64MB of heap

Total PUTs so far    = 0
Total KEYs CLEARED so far    = 77982
Total VALUESs CLEARED so far = 77980
100000
24MB

Total PUTs so far    = 100000
Total KEYs CLEARED so far    = 134616
Total VALUESs CLEARED so far = 134614
100000
53MB

Total PUTs so far    = 200000
Total KEYs CLEARED so far    = 221489
Total VALUESs CLEARED so far = 221488
100000
157MB

Total PUTs so far    = 300000
Total KEYs CLEARED so far    = 366966
Total VALUESs CLEARED so far = 366966
100000
77MB

Total PUTs so far    = 400000
Total KEYs CLEARED so far    = 366968
Total VALUESs CLEARED so far = 366967
100000
129MB

Total PUTs so far    = 500000
Total KEYs CLEARED so far    = 533883
Total VALUESs CLEARED so far = 533881
100000
50MB

Total PUTs so far    = 600000
Total KEYs CLEARED so far    = 533886
Total VALUESs CLEARED so far = 533883
100000
6MB

Total PUTs so far    = 700000
Total KEYs CLEARED so far    = 775763
Total VALUESs CLEARED so far = 775762
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at Referencestest.weak(Referencestest.java:38)
    at Referencestest.main(Referencestest.java:21)
like image 205
Aravind Yarram Avatar asked Jan 13 '10 21:01

Aravind Yarram


3 Answers

from http://weblogs.java.net/blog/2006/05/04/understanding-weak-references

I think your use of HashMap is likely to be the issue. You might want to use WeakHashMap

To solve the "widget serial number" problem above, the easiest thing to do is use the built-in WeakHashMap class. WeakHashMap works exactly like HashMap, except that the keys (not the values!) are referred to using weak references. If a WeakHashMap key becomes garbage, its entry is removed automatically. This avoids the pitfalls I described and requires no changes other than the switch from HashMap to a WeakHashMap. If you're following the standard convention of referring to your maps via the Map interface, no other code needs to even be aware of the change.

like image 183
DaveC Avatar answered Nov 05 '22 06:11

DaveC


The heart of the problem is probably that you're filling your heap with WeakReference-objects, the weak references are cleared when you're getting low on memory, but the reference objects themselves are not, so your hashmap is filling up with boat-load if WeakReference objects (not to mention the object array the hashmap uses, which will grow indefinitely), all pointing to null.

The solution, as already pointed out, is a weak hashmap, which will clear out those objects if they're no longer in use (this is done during put).

EDIT:
As Kevin pointed out, you already have your reference-queue logic worked out (I didn't pay close enough attention), a solution using your code is to just clear it out of the map at the point where the key has been collected. This is exactly how weak hash map works (where the poll is simply triggered on insert).

like image 21
falstro Avatar answered Nov 05 '22 06:11

falstro


Even when your weak references let go of the things they are referencing, they still do not get recycled themselves.

So eventually your hash will fill up with references to nothing and crash.

What you would need (if you wanted to do it this way) would be to have an event triggered by object deletion that went in and removed the reference from the hash. (which would cause threading issues you need to be aware of as well)

like image 1
Bill K Avatar answered Nov 05 '22 07:11

Bill K