Is there another way besides a fixed timeout to know when a WeakHashMap has updated its entries after a key becomes weakly reachable?
For example this code which nulls the strong reference to the key still prints that the entry is in the map unless a timeout is added.
WeakHashMap<Integer, Integer> map = new WeakHashMap<>();
Integer i = 1000;
map.put(i, 1);
System.out.println(map.size()); // prints 1
i = null;
System.gc();
//Thread.sleep(0);
System.out.println(map.size()); // without sleep prints 1, with sleep prints 0
Is there a more elegant way to know when the WeakReferences have finished updating?
I don't think there is a good way to do it. There are a few problems:
The cleaning of stale entries from the
WeakHashMapis an internal mechanism that involves aReferenceQueuethat is private to the implementation. You would need to break encapsulation to access thequeuefield.OK that is possible, but you would be making your code Java version specific ... in theory.
There is no good way for something other than
WeakHashMapitself to inspect the queue.ReferenceQueueonly provides three public operations:poll(),remove()andremove(time_out). These will all remove an element from the queue (if or when there is one). But if anything other than theWeakHashMapremoves an element, that element won't be processed.This effectively means that you cannot detect when there is stuff on the queue without "breaking" it.
OK so you could break abstraction on the
ReferenceQueueas well to access itsheadfield.Even if you do the above, you would still need to poll the
ReferenceQueue.headfield to detect when the queue becomes non-empty and empty again.The code in
WeakHashMapthat processes the queue does not run spontaneously. (There is no cleaner thread.) It actually runs when something performs an operation (e.g.get,put,size,clearand so on) on theWeakHashMap. The details are liable to be version specific.This means the "event" that your code is trying to detect is only going to happen when you call
get. So if you only want to callgetafter the entry has been removed, you have a "loop" in the temporal dependencies.Finally, it is not guaranteed that calling
System.gc()will run immediately, (or at all), or that it will detect that given unreachable object is unreachable1. And even if it does detect this, it is not guaranteed that theWeakReferencewill be broken in a timely fashion.So you should not code your application to depend on those things ... via predictable cleaning of the map. (And if the real use-case for this just unit testing, I don't think this is worth spending the time on this. The
sleepworkaround is fine ... assuming it is reliable enough.)1 - For example, if the object has been tenured and the
System.gc()call only triggers an minor collection, then the object would not be detected as unreachable.