Christopher Francisco
Christopher Francisco

Reputation: 16278

Understanding WeakReference

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.

  1. Am I correct?

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.

  1. Am I still correct?

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

  1. Am I still still correct?

Upvotes: 0

Views: 324

Answers (2)

Smith_61
Smith_61

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

chubbsondubs
chubbsondubs

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

Related Questions