This week's book giveaway is in the Kotlin forum.
We're giving away four copies of Kotlin in Action and have Dmitry Jemerov & Svetlana Isakova on-line!
See this thread for details.
Win a copy of Kotlin in Action this week in the Kotlin forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic

A question on WeakHashMap - From Oracle docs.  RSS feed

 
Chan Ag
Rancher
Posts: 1090
14
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hello,

The API doc for WeakHashMap mentions the following..

Implementation note: The value objects in a WeakHashMap are held by ordinary strong references. Thus care should be taken to ensure that value objects do not strongly refer to their own keys, either directly or indirectly, since that will prevent the keys from being discarded. Note that a value object may refer indirectly to its key via the WeakHashMap itself; that is, a value object may strongly refer to some other key object whose associated value object, in turn, strongly refers to the key of the first value object.
One way to deal with this is to wrap values themselves within WeakReferences before inserting, as in: m.put(key, new WeakReference(value)), and then unwrapping upon each get.


I have a question on the above and I'd be glad if you can help me understand it better.

My question is this - if I wrap a value object within a WeakReference before inserting it into the HashMap, isn't it possible that this value could be finalized and reclaimed even before its key is reclaimed or finalized? Would it be possible? When I tested it with my test code, I didn't have any key return null value. I ran my code several times, but I couldn't create the case where the value was finalized before its key was finalized and reclaimed. So should this imply that the value objects that are wrapped in a WeakReference would always stay for at least as long as the key is accessible?

Chan.
 
Maxim Karvonen
Ranch Hand
Posts: 121
12
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi, Chan.

You should be very careful about what you mean using word "value"! For your weak map value is a WeakReference and not an object! For all other business code "weak reference" is just a "wrapper" and "implementation detail" and word "value" may mean "content of the reference". So there will be a difference. WeakReference (hash-map value) won't be finalized until it's key is eligible for GC. But content of the WeakReference may be reclaimed if there are no other references to it.

Look at the following example. It shows difference between "value as a content of a weak reference" and "value as a hash-map value".
 
Henry Wong
author
Sheriff
Posts: 23283
125
C++ Chrome Eclipse IDE Firefox Browser Java jQuery Linux VI Editor Windows
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Chan Ag wrote:
My question is this - if I wrap a value object within a WeakReference before inserting it into the HashMap, isn't it possible that this value could be finalized and reclaimed even before its key is reclaimed or finalized? Would it be possible? When I tested it with my test code, I didn't have any key return null value. I ran my code several times, but I couldn't create the case where the value was finalized before its key was finalized and reclaimed. So should this imply that the value objects that are wrapped in a WeakReference would always stay for at least as long as the key is accessible?


You can try to force it... place the value into a weak reference and insert the key-value pair (with the value indirectly held in that weak reference) into the weak hashmap. Outside of the weak hashmap, make sure that you still have a strong reference to the key, but do *not* have any strong references to the value. Do a bunch of garbage collections, and check to see if the value has been collected.

Since the value has no strong references, it is possible to be collected. On the other hand, the key does have a strong reference outside of the weak hashmap and won't be able to be collected.


Also, note, don't check the value to often. Try to cause a few garbage collections, and give it lots of time, before checking. Checking for the existence of the value will temporarily make a strong reference to the value (and hence, prevent a garbage collection).

Henry
 
Steve Luke
Bartender
Posts: 4181
22
IntelliJ IDE Java Python
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
One last thing, if this is related to the code in your other question, you used Integers as values, I think. If you still do that, make sure you use the constructor to create the Integer (rather than autoboxing) to avoid using cached values (and as such, having strong references to them outside your control). If I recall, in that code you used autoboxing (and only used values in the range 0 to 10).
 
Chan Ag
Rancher
Posts: 1090
14
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thank you so much, All.

I think now I understand that if the value of the keys are WeakReferences, the objects they are wrapping could still be null if there are no strong references to them inside/outside the map.

I've added a few more checks to the example posted above. Here is my slightly modified code.



I got the following output for the above code.
run:
EntrySet : [z=java.lang.ref.WeakReference@321ea24, y=java.lang.ref.WeakReference@2792e317, x=java.lang.ref.WeakReference@1c7b0f4d]
Printing the value for the key x.
Logical content :y
Physical content :java.lang.ref.WeakReference@1c7b0f4d
Printing the value for the key z.
Logical content :w
Physical content :java.lang.ref.WeakReference@321ea24
------------------------------------
EntrySet : [z=java.lang.ref.WeakReference@321ea24, x=java.lang.ref.WeakReference@1c7b0f4d]
Printing the value for the key x after gc.
Logical content :null
Physical content :java.lang.ref.WeakReference@1c7b0f4d
Printing the value for the key z.
Logical content :null
Physical content :java.lang.ref.WeakReference@321ea24
------------------------------------
java.lang.ref.WeakReference@2792e317
java.lang.ref.WeakReference@1c7b0f4d
java.lang.ref.WeakReference@321ea24
x
null
null
BUILD SUCCESSFUL (total time: 2 seconds)

I think all of it is making sense now. WeakReferences as WeakReferences types are alive cause they are created strongly. But the objects they wrap exist for as long as they have Strong references, either inside or outside the map ( until GC has not claimed them after there are no strong references to them). The fact that they are wrapped by WeakReferences does not prevent them from being garbage collected. Also the keys of a WeakHashMap are also weak references, in that their presence in the map as keys does not prevent them from being garbage collected.

So the objects my WeakReferences were wrapping got garbage collected even though the keys were present in the map.

I tested the case of having strong references for keys and values too. Here is the code.


For this one, I got the following output.
run:
EntrySet : [x=y, y=x, z=null]
------------------------------------
EntrySet : [x=y, y=x]
BUILD SUCCESSFUL (total time: 5 seconds)

I think it's all making sense now. Thanks a lot.

Chan.
 
Henry Wong
author
Sheriff
Posts: 23283
125
C++ Chrome Eclipse IDE Firefox Browser Java jQuery Linux VI Editor Windows
  • Likes 2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Chan Ag wrote:
I think now I understand that if the value of the keys are WeakReferences, the objects they are wrapping could still be null if there are no strong references to them inside/outside the map.


To complete the picture... when a key get GC'ed, meaning the original key object gets GC'ed, then the key-value pair entry is no longer valid. There is an entry for it in the map -- and in the entry there is a weakreference (that is no longer pointing to the key object), a strong reference to the value, and even a cached hashcode for the entry. However, since the original key object is no longer available, there is no way to obtain the key, and hence, no way to run the equals() method on it anymore. The entry can't be used anymore.

The weak hashmap class also use the weak reference queue feature of weak references. When the garbage collector determines that an instance only has week references, it will finalize the instance, and null out the referent of the weak references. Also, if a queue is assigned, then affected weak references will be placed in the queue. The weak hashmap class assigns the weak references that it creates to an internal queue.

Later, when your application does some operations, such as put() or remove(), it will check the queue -- and if there are weak references in it, it will remove the entries that has those weak reference objects as its key. Basically, as you use the hashmap, it will clean up the invalid key-valid pair entries, which clean up the weak reference instances and the value instances.

Chan Ag wrote:
I think all of it is making sense now. WeakReferences as WeakReferences types are alive cause they are created strongly. But the objects they wrap exist for as long as they have Strong references, either inside or outside the map ( until GC has not claimed them after there are no strong references to them). The fact that they are wrapped by WeakReferences does not prevent them from being garbage collected. Also the keys of a WeakHashMap are also weak references, in that their presence in the map as keys does not prevent them from being garbage collected.

So the objects my WeakReferences were wrapping got garbage collected even though the keys were present in the map.


As you probably noticed, there is no reason to make the value wrapped in weak references. The weak hashmap doesn't track them, meaning doesn't know that it has been collected -- your application is responsible to deal with it when the value returned is null.

It is best to keep them as strong references in the map, which means that they will be cleaned up when the entry is clean up. So, sometime after a GC cycle when the key is gone, after you use the hashmap somewhat, the entry will be removed which will clean up the value too.

Henry
 
Chan Ag
Rancher
Posts: 1090
14
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks again, Henry.

Chan.
 
Steve Luke
Bartender
Posts: 4181
22
IntelliJ IDE Java Python
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Great explanation Henry I get so confused with these sorts of things. Have a cow
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!