8

In Java there is a data structure called a WeakHashMap that stores weak references as keys. Whenever the weak references are taken out of memory the entry is removed from the map.

If I have a data structure such as a Stack or a Set where I am storing weak references, will their entries be automatically removed when the weak reference is taken out of memory?

Below is an example of a Stack that stores weak references.

Stack<WeakReference<Object>> objStack = new Stack<WeakReference<Object>>();
  • Are you sure it won't return a null value instead? – zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz Aug 10 '12 at 21:52
  • 1
    If you're thinking of using weak references for a real application, please **don't do it**! The people who build the core library regret adding them as they don't offer any benefit. Watch this [presentation](http://www.infoq.com/presentations/Extreme-Performance-Java) if you're interested. – Augusto Aug 10 '12 at 21:53
  • 1
    Thanks for the video link but I think there are some projects that make good use of WeakReferences. For example check out the Robotium project for Android http://code.google.com/p/robotium/ – zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz Aug 10 '12 at 21:56
  • 1
    Mmm, I 'm quite sure I got confused with SoftReferences. Thanks for the link to the project, I'll check it out. – Augusto Aug 10 '12 at 22:00

2 Answers2

2

Yes. What you're describing is a property of weak references in general, not WeakHashMap specifically.

From the API:

Suppose that the garbage collector determines at a certain point in time that an object is weakly reachable. At that time it will atomically clear all weak references to that object...

Pops
  • 30,199
  • 37
  • 136
  • 151
  • So if you have a LinkedList of WeakReferences and one of the WeakReferences in the middle of the list goes out of memory, your LinkedList will work as expected? – zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz Aug 10 '12 at 21:58
  • I'm not sure how to answer that... it depends on what you consider to be "as expected." – Pops Aug 10 '12 at 22:02
  • 1
    I think what I didn't understand is when you call .get() on a WeakReference it gets the actual object. The WeakReference is never set to null just the object it points to. – zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz Aug 10 '12 at 22:04
  • Ah, gotcha. Well, now you know! – Pops Aug 10 '12 at 22:05
  • 1
    @zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz Yes, that's right (about `get()`). If you then want to get rid of the now-"empty" `WeakReference`, you'll need to do that manually. If you look at the code for [WeakHashMap](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/WeakHashMap.java#WeakHashMap.expungeStaleEntries%28%29), you'll see that's what they do. – yshavit Aug 10 '12 at 22:28
  • Seems like you would want to first declare your objects (the values) as WeakReference before you add them to WeakHashMap, that way after the key is garbage collected then the value reference will also be weakly collected (otherwise manual cleanup would be necessary)? Am I thinking correctly? – djangofan Feb 01 '13 at 17:37
1

Better late than never.

This is not how weak references work. The WeakReference-object will always be there, but its get()-method may return the original object or null (if the original object was GC:ed).

This means your Stack will always contain a WeakReference-object.

Here's a verbose test I did (however it depends on the JVM doing GC in the middle, which may or may not happen "on command" - try to add some more System.gc():s if it doesn't happen):

public static void main(String[] args) {
    // Set up
    Object object = new Object();
    final WeakReference<Object> weakReference = new WeakReference<>(object);
    final Stack<WeakReference<Object>> references = new Stack<>();      
    references.add(weakReference);

    // Before
    System.out.println("Before:");

    references.forEach(r -> {
        System.out.println("Reference: " + r);
        System.out.println("Object: " + r.get());
    });

    // GC
    object = null;

    System.gc();
    System.gc();
    System.gc();

    // After
    System.out.println("After:");
    references.forEach(r -> {
        System.out.println("Reference: " + r);
        System.out.println("Object: " + r.get());
    });
}

The output:

Before:
Reference: java.lang.ref.WeakReference@238e0d81
Object: java.lang.Object@31221be2
After:
Reference: java.lang.ref.WeakReference@238e0d81
Object: null

Your best approach is probably to just ignore entries where get() returns null once you "pop" them.

Side note: My first test was with a String-object, which never got GC:ed. This is because strings in Java are "interned" (see String.intern()) meaning there is a hidden cache of static strings that are reused. A string will never be GC:ed, unless that pool of strings are full (or something along that line...) I think strings are the only Java object with this "special" characteristic but I'd recommend always using custom made objects in a WeakReference to make sure it gets GC:ed correctly.

Erk
  • 1,159
  • 15
  • 9