WeakHashMap and strongly referenced value
Asked Answered
V

1

9

Javadocs says "When a key has been discarded its entry is effectively removed from the map".

But unless there is another thread that occasionally removes such Map.Entry entries, won't the value objects be strongly referenced by the map? But since there is no such thread running, only the get method invocations can remove such entries - one at a time.

I almost always use WeakHashMap<K, WeakReference<V>> for that reason. Why would they not have made that the default behavior - values as weak references too?

Vitriform answered 6/2, 2012 at 20:14 Comment(7)
https://mcmap.net/q/57157/-reduce-memory-usage-with-weakhashmap is almost identical a question. But I wanted to know if my assertion is correct: an entry will be removed only when get() discovers that the key has been gc'ed, which frankly doesn't seem to be of great value unless I use WeakReferences as values.Vitriform
Yeah, it's just polled. If you want something different, write your own version.Fridlund
If you need something different, use a library -- writing your own is super tricky. Guava has MapMaker, which lets you configure the strength of key and value references: docs.guava-libraries.googlecode.com/git-history/release/javadoc/…Observer
From ibm.com/developerworks/java/library/j-jtp11225 I think that WeakHashMap is half-baked, why'd they poll the queue with expungeStaleEntries() as part of most methods and not have a dedicated thread that does blocking reads?Vitriform
@Ustaman Sangat WeakHashMap polls a ReferenceQueue.Fridlund
@TomHawtin-tackline, you mean there is a dedicated thread that does the polling regularly? I thought the polling was part of most interactions with the Map. I don't see any such thread being created though with Sun's JDK.Vitriform
@Ustaman Sangat If there was a dedicated thread (or even if it used Cleaners), it wouldn't need to poll.Fridlund
P
13

Reference queues are used to automatically remove entries.

http://docs.oracle.com/javase/1.4.2/docs/api/java/lang/ref/ReferenceQueue.html

Reference queues, to which registered reference objects are appended by the garbage collector after the appropriate reachability changes are detected.

Basically, weak references are a core part of the garbage collector, so when a GC sweep happens, unused references are found and put onto queues and then action can be taken based on the content of those queues.

A thread can sit on the queue's remove method to be alerted when cleanup needs to be done or poll the queue.

"Java theory and practice: Plugging memory leaks with weak references" explains:

The implementation of WeakHashMap illustrates a common idiom with weak references -- that some internal object extends WeakReference.

...

WeakHashMap uses weak references for holding map keys, which allows the key objects to be garbage collected when they are no longer used by the application, and the get() implementation can tell a live mapping from a dead one by whether WeakReference.get() returns null. But this is only half of what is needed to keep a Map's memory consumption from increasing throughout the lifetime of the application; something must also be done to prune the dead entries from the Map after the key object has been collected. Otherwise, the Map would simply fill up with entries corresponding to dead keys. And while this would be invisible to the application, it could still cause the application to run out of memory because the Map.Entry and value objects would not be collected, even if the key is.

...

Reference queues are the garbage collector's primary means of feeding back information to the application about object lifecycle. Weak references have two constructors: one takes only the referent as an argument and the other also takes a reference queue. When a weak reference has been created with an associated reference queue and the referent becomes a candidate for GC, the reference object (not the referent) is enqueued on the reference queue after the reference is cleared. The application can then retrieve the reference from the reference queue and learn that the referent has been collected so it can perform associated cleanup activities, such as expunging the entries for objects that have fallen out of a weak collection. (Reference queues offer the same dequeuing modes as BlockingQueue -- polled, timed blocking, and untimed blocking.)

EDIT:

Even with queues, weak maps can still leak. Ephemerons are an attempt to solve the case where a weak key references a strongly held value that references the key. They are not implementable in java.

Ephemerons solve a problem which is commonly found when trying to "attach" properties to objects by using a registry. When some property should be attached to an object, the property should (in terms of GC behavior) typically have the life-time that an instance variable of this object would have. However, this is complicated by having an external association between the object and its property such as:

property --------- registry --------- association --------- object

Here, the registry (a third party) will hold onto the association itself which would require manual removal from the registry (instead of automated garbage collection). While this problem can always be solved in any given concrete situation by using one of the various weak association types, choosing the 'right' kind of association depends on a variety of factors some of which can change dynamically.

Ephemerons solve this problem by defining that the 'contents' (value) of an ephemeron will be held strongly until the key is known to be garbage collected. From then on, the contents of the ephemeron will be held weakly. Therefore, the contents of an ephemeron can become eligible for garbage collection if and only if the key is garbage collectable which is the exact behavior which we would observe for an instance variable of the object.

Packing answered 6/2, 2012 at 20:22 Comment(5)
Forgot about the reference queue's ability to call our own hook.Vitriform
I found from your link "WeakHashMap has a private method called expungeStaleEntries() that is called during most Map operations." to be useful. And wonder if spawning a thread which would actually do blocking reads on the reference queue might be a better idea. Maybe that is what Guava's MapMaker does.Vitriform
@UstamanSangat, I don't know what MapMaker does, but maybe expungeStaleEntries() polls the reference queue to amortize cleanup with use of the map. I would try using something like MapMaker before mucking around with implementing my own key cleaning. Btw, I added an edit that points out ways you can leak memory even using weak key maps.Packing
I thought of ephemeron as a concept but it seems like you are suggesting something concrete at the JVM?Vitriform
@UstamanSangat, My understanding is that ephemerons typically have to be designed into the memory semantics. The next version of EcmaScript will have them. Stock JVMs do not.Packing

© 2022 - 2024 — McMap. All rights reserved.