For a library that includes asynchronous operations, I need to keep a reference to the object until a certain condition is met.
(I know this sounds unusual. So, here is some context, although it may not be strictly relevant: an object can be considered a direct ByteBuffer
, which is used in JNI operations. JNI operations will retrieve the buffer address. At this point, this address is only a "pointer" that is not considered a reference to a byte buffer. The address can be used asynchronously, later in time. Thus, the buffer should be prevented from garbage collection until the JNI operation is complete.)
To achieve this, I applied a method that is basically equivalent to this:
private static void keepReference(final Object object) { Runnable runnable = new Runnable() { @SuppressWarnings("unused") private Object localObject = object; public void run() {
The idea is to create an instance of Runnable
, which has the required object as a field, throws it into the executable file and allows runnable to wait until the condition is met. The contractor will reference the runnable instance until it is finished. Runnable is supposed to keep a reference to the required object. Thus, only after the condition is satisfied, the runnable will be released by the executor, and thus the local object will become suitable for garbage collection.
The localObject
field localObject
not used in the body of the run()
method. Could this compiler (more precisely: runtime) detect this and decide to delete this unused link and thus make it too difficult to collect garbage earlier?
(I examined workarounds for this. For example, using an object in a "dummy statement", for example, logger.log(FINEST, localObject);
but even then you couldn’t be sure that the smart optimizer won’t do some embedding and that’s all still discovering that the object is really not in use)
Update . As pointed out in the comments: whether this can work at all may depend on the exact implementation of the Executor
(although I will have to analyze it more carefully). In this case, the executor will be ThreadPoolExecutor
.
This may be one step to the answer:
ThreadPoolExecutor
has an afterExecute
method. You can override this method and then use the reflection sledgehammer to dive into the Runnable
instance, which is given there as an argument. Now you can simply use reflective hacks to go to this link and use runnable.getClass().getDeclaredFields()
to retrieve the fields (namely, the localObject
field), and then get the value of this field. And I think that one should not be allowed to observe there a value that differs from that which it originally had.
Another comment noted that the default implementation afterExecute
empty, but I'm not sure if this fact can affect the question of whether the field can be deleted or not.
Now I strongly suggest that the field may not be deleted. But some specific reference (or at least more convincing arguments) would be nice.
Update 2 . Based on Holger's comments and answer, I think that not deleting the "field itself" might be a problem, but rather the GC of the surrounding Runnable
instance. So right now, I guess you can try something like this:
private static long dummyCounter = 0; private static Executor executor = new ThreadPoolExecutor(...) { @Override public void afterExecute(Runnable r, Throwable t) { if (r != null) dummyCounter++; if (dummyCounter == Long.MAX_VALUE) { System.out.println("This will never happen", r); } } }
to make sure that localObject
in runnable really lives as long as needed. But I almost can’t remember that I was ever forced to write something that shouted "hacking" as loudly as these few lines of code ...