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

StackOverflow https://stackoverflow.com/questions/15567221

سؤال

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;
    }
}
}
هل كانت مفيدة؟

المحلول

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.

نصائح أخرى

Because size is a int field inside map implementation and GC doesn't decrement it after removing WeakReference

Look at WeakHashMap#size() implementation for details, WeakHashMap stores queue of collected WeakReferences and recount size size on demand

Source : http://weblogs.java.net/blog/2006/05/04/understanding-weak-references Reference queues

Once a WeakReference starts returning null, the object it pointed to has become garbage and the WeakReference object is pretty much useless. This generally means that some sort of cleanup is required; WeakHashMap, for example, has to remove such defunct entries to avoid holding onto an ever-increasing number of dead WeakReferences.

The ReferenceQueue class makes it easy to keep track of dead references. If you pass a ReferenceQueue into a weak reference's constructor, the reference object will be automatically inserted into the reference queue when the object to which it pointed becomes garbage. You can then, at some regular interval, process the ReferenceQueue and perform whatever cleanup is needed for dead references.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top