Reputation: 16278
I believe I have finally understood WeakReference
and I wanna make sure I'm not mistaken.
Consider a Map<Client,Callback> clientMap
that is a private
attribute of a Singleton
class called MyService
that we use to map registered clients to their respective callbacks.
Now consider the following code:
// We register a client and it's callback
MyService.getInstance().register(client,callback);
// .. some code happens ..
// At this point, we decide client no longer lives anymore
client = null;
Up to this point, even though client
is now pointing to null
, the clientMap
stills has a reference to the address client
was previously pointing to, causing a memory leak.
If question 1 is YES, Then continue: we decide that to prevent memory leak, we should remove the object from the client map too:
client = null;
MyService.getInstance().unregister(client); // This methods calls Map#remove(Client)
Now we have fixed the memory leak.
If question 2 is YES: Due to some unexplained issues, we can't really call Map#remove()
so this is where WeakReference
comes in.
By using Map<WeakReference<Client>, WeakReference<Callback>> clientMap
, the reference counter won't increase, and once we set client = null;
it automatically deletes it from the clientMap
Upvotes: 0
Views: 324
Reputation: 2088
Yes and no. When all references, not including weak references, an object is available for garbage collection on the next sweep. In your case it would not work as expected because the Map would still contain the entry for a WeakReference that points to nothing and is mapped to a WeakReference that points to nothing, with I believe no way to remove that element without iterating over all keys/values.
The second problem with your last example is that Callback is now also a WeakReference meaning it can be garbage collected, even if it is still in use, as long as no other references exist to it. Which doesn't sound like what you want.
Client client = ...;
Callback callback = ...;
Map< WeakReference< Client >, WeakReference< Callback > > map = ...;
map.put( new WeakReference< Client >( client ),
new WeakReference< Callback >( callback ) );
callback = null;
// Uh oh, callback can now be collected but client is still connected
The best solution is to explicitly remove the elements from all collections when no longer needed. This removes any reliance on the GC which you never really want to rely on, and there is no dangling entries in collections that will need to be cleaned up later.
Another solution is to use a WeakHashMap. This appears to do exactly what you want, it will handle any dangling entries when the key is reclaimed. It does come with it's own pitfalls. It doesn't guarantee when those entries will be reclaimed, and it will not work with circular references as values are still referenced through a strong reference. Meaning if Callbacks keep a reference to their Client it will never be reclaimed as the Callback is strongly reference which strongly references the Client, and so on.
To clarify why the 'when' is an issue with WeakHashMap, is that WeakHashMap can cleanup dangling entries whenever it wants. As long as it get's done without developer intervention. This means that values can still be strongly referenced long after they are no longer needed.
Upvotes: 0
Reputation: 38706
Well that's why WeakHashMap exists. No need to do Map<WeakReference<Blah>,<WeakReference<Blah>>. Just use WeakHashMap, and it will clean itself up. In WeakHashMap it's the keys that are weakly held onto which is fine but the value is a hard reference. That is fine because the value will get dropped when the key gets reclaimed. Read the docs for warnings why this matters.
Now true WeakReference will not prevent the garbage collector from cleaning up memory it points to. When it happens is up to the VM. Typically, it will clean it up very quickly, but under load it will let it hang around until it absolutely has to let memory go. It's the last thing the garbage collector does when it's cleaning up memory (If I remember correctly).
Be careful with registering callbacks this way because the typically most callbacks are register and forget as in forget the reference therefore, only one thing holds a reference to the callback which is the weak ref. And it gets GC'ed almost immediately without calling you back. So if you do this make sure you hold onto the callback somewhere else.
Upvotes: 1