May the removal of an unused field cause a garbage collection? May the removal of an unused field cause a garbage collection? multithreading multithreading

May the removal of an unused field cause a garbage collection?


If JNI code fetches the address of a direct buffer, it should be the responsibility of the JNI code itself, to hold a reference to the direct buffer object as long as the JNI code holds the pointer, e.g. using NewGlobalRef and DeleteGlobalRef.

Regarding your specific question, this is addressed directly in JLS §12.6.1. Implementing Finalization:

Optimizing transformations of a program can be designed that reduce the number of objects that are reachable to be less than those which would naively be considered reachable. …

Another example of this occurs if the values in an object's fields are stored in registers. … Note that this sort of optimization is only allowed if references are on the stack, not stored in the heap.

(the last sentence matters)

It is illustrated in that chapter by an example not too different to yours. To make things short, the localObject reference within the Runnable instance will keep the life time of the referenced object at least as long as the life time of the Runnable instance.

That said, the critical point here is the actual life time of the Runnable instance. It will be considered definitely alive, i.e. immune to optimizations, due to the rule specified above, if it is also referred by an object that is immune to optimizations, but even an Executor isn’t necessarily a globally visible object.

That said, method inlining is one of the simplest optimizations, after which a JVM would detect that the afterExecute of a ThreadPoolExecutor is a no-op. By the way, the Runnable passed to it is the Runnable passed to execute, but it wouldn’t be the same as passed to submit, if you use that method, as (only) in the latter case, it’s wrapped in a RunnableFuture.

Note that even the ongoing execution of the run() method does not prevent the collection of the Runnable implementation’s instance, as illustrated in “finalize() called on strongly reachable object in Java 8”.

The bottom line is that you will be walking on thin ice when you try to fight the garbage collector. As the first sentence of the cite above states: “Optimizing transformations of a program can be designed that reduce the number of objects that are reachable to be less than those which would naively be considered reachable.” Whereas we all may find ourselves being thinking too naively…

As said at the beginning, you may rethink the responsibilities. It’s worth noting that when your class has a close() method which has to be invoked to release the resource after all threads have finished their work, this required explicit action is already sufficient to prevent the early collection of the resource (assuming that the method is indeed called at the right point)…


Execution of Runnable in a thread pool is not enough to keep an object from being garbage collected. Even "this" can be collected! See JDK-8055183.

The following example shows that keepReference does not really keep it. Though the problem does not happen with vanilla JDK (because the compiler is not smart enough), it can be reproduced when a call to ThreadPoolExecutor.afterExecute is commented out. It is absolutely possible optimization, because afterExecute is no-op in the default ThreadPoolExecutor implementation.

import java.lang.ref.WeakReference;import java.util.concurrent.*;public class StrangeGC {    private static final ExecutorService someExecutor =        Executors.newSingleThreadExecutor();    private static void keepReference(final Object object) {        Runnable runnable = new Runnable() {            @SuppressWarnings("unused")            private Object localObject = object;            public void run() {                WeakReference<?> ref = new WeakReference<>(object);                if (ThreadLocalRandom.current().nextInt(1024) == 0) {                    System.gc();                }                if (ref.get() == null) {                    System.out.println("Object is garbage collected");                    System.exit(0);                }            }        };        someExecutor.execute(runnable);    }    public static void main(String[] args) throws Exception {        while (true) {            keepReference(new Object());        }    }}

Your hack with overriding afterExecute will work though.
You've basically invented a kind of Reachability Fence, see JDK-8133348.

The problem you've faced is known. It will be addressed in Java 9 as a part of JEP 193. There will be a standard API to explicitly mark objects as reachable: Reference.reachabilityFence(obj).

Update

Javadoc comments to Reference.reachabilityFence suggest synchronized block as an alternative construction to ensure reachability.