The problem with imglib2-cache has nothing to do with
There, the problem is that the semantics of
PhantomReference slightly changed between java 8 and 9
(See the difference between the javadocs:
Let me first explain the task that needs to be solved in imglib2-cache (and then why we use PhantomReferences to do it, and where that breaks now):
In imglib2-cache, when an entry (lets say a
Cell from a
CellImg) is thrown out of the
CacheRemover is notified – so that it can write the
Cell out to disk for example. The problem is that we do not want to throw anything out of the cache while it is still in use. If a
Cell A and we remove it from the cache (and write it to disk), then changes to
Cell A after that are lost. Worse: If another
RandomAccess now comes and wants
Cell A again, it will get a different object, and now two competing versions of reality exist.
In most “normal” caching scenarios, this is a non-issue, because the entries in the cache are immutable things (with
equals() equality). But we have modifiable entries (with ‘==’ equality).
Anyway, so unless we want to do reference counting (let clients check entries out and back in to the cache), we need to rely on the garbage collector.
The GC tells us when the entry can be collected, and then we can be sure that it is no longer in use. However, before it is really garbage collected, we need to get to the entry and write it to disk. So basically we need to bring a dead object back to life. This is obviously evil and we need to be careful to really only use the “zombie Cell” to write it to disk and not let it get back out to a
The way imglib2-cache does it, is by keeping
PhantomReferences to the entries. When an entry is (about to be) garbage collected, its
PhantomReference is enqueued in a reference queue where we retrieve it.
The javadoc says
In order to ensure that a reclaimable object remains so, the referent of a phantom reference may not be retrieved
However that was not really true: you still could get to the referent via reflection.
And the referent never was
null, because the javadoc also said
Unlike soft and weak references, phantom references are not automatically cleared by the garbage collector as they are enqueued. An object that is reachable via phantom references will remain so until all such references are cleared or themselves become unreachable.
So basically, until we process the queue, the stuff will stay around.
That has changed, the java 9 javadoc no longer has that last paragraph. We still can get to the referent via reflection, but now its
It’s fine that this changed, what we did was a bit fishy anyway.
Now we need to find a workaround.
I see no way to do it without changing the imglib2-cache API.
Maybe I should say something about how
PhantomReferences are typically used in this scenario: Assume you have object
A that you want to do something with (write to disk…) when its garbage collected. But actually, all that you care about is some data contained in
A (lets say a field
B b). Now you derive a
PhantomReference<A> subclass that also has the
b field. So even after
A is gone for good, you still have the data required to write “
The nice thing about current imglib2-cache API is, that it works (worked) without putting any restrictions on what
T you could put in the cache. But to do the above trick, now we probably need some
T extends Cacheable that gives us access to the
Internals so that we can put those in our PhantomReference. The writer for
T now either needs to be able to write
Internal, or we need a way to recreate a
Internal. Clients of the cache have to promise to never hold a reference to
Internal when they don’t also hold a reference to
T, etc. That all doesn’t sound so nice…
I don’t really think we need anything changed in the JDK or something like that. We just need a good idea. Basically we need a “special” reference to T and be notified if only “special” references to T are left.