ObjectCleanerThread must be a deamon thread to ensure the JVM can always terminate.

Motivation:

The ObjectCleanerThread must be a daemon thread as otherwise we may block the JVM from exit. By using a daemon thread we basically give the same garantees as the JVM when it comes to cleanup of resources (as the GC threads are also daemon threads and the CleanerImpl uses a deamon thread as well in Java9+).

Modifications:

Change ObjectCleanThread to be a daemon thread.

Result:

JVM shutdown will always be able to complete. Fixed [#7617].
This commit is contained in:
Norman Maurer 2018-01-25 13:48:59 +01:00
parent f99a1985ce
commit 1879433ae6
2 changed files with 30 additions and 6 deletions

View File

@ -16,8 +16,6 @@
package io.netty.util.internal; package io.netty.util.internal;
import io.netty.util.concurrent.FastThreadLocalThread; import io.netty.util.concurrent.FastThreadLocalThread;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.lang.ref.ReferenceQueue; import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@ -36,11 +34,13 @@ import static java.lang.Math.max;
public final class ObjectCleaner { public final class ObjectCleaner {
private static final int REFERENCE_QUEUE_POLL_TIMEOUT_MS = private static final int REFERENCE_QUEUE_POLL_TIMEOUT_MS =
max(500, getInt("io.netty.util.internal.ObjectCleaner.refQueuePollTimeout", 10000)); max(500, getInt("io.netty.util.internal.ObjectCleaner.refQueuePollTimeout", 10000));
// Package-private for testing
static final String CLEANER_THREAD_NAME = ObjectCleaner.class.getSimpleName() + "Thread";
// This will hold a reference to the AutomaticCleanerReference which will be removed once we called cleanup() // This will hold a reference to the AutomaticCleanerReference which will be removed once we called cleanup()
private static final Set<AutomaticCleanerReference> LIVE_SET = new ConcurrentSet<AutomaticCleanerReference>(); private static final Set<AutomaticCleanerReference> LIVE_SET = new ConcurrentSet<AutomaticCleanerReference>();
private static final ReferenceQueue<Object> REFERENCE_QUEUE = new ReferenceQueue<Object>(); private static final ReferenceQueue<Object> REFERENCE_QUEUE = new ReferenceQueue<Object>();
private static final AtomicBoolean CLEANER_RUNNING = new AtomicBoolean(false); private static final AtomicBoolean CLEANER_RUNNING = new AtomicBoolean(false);
private static final String CLEANER_THREAD_NAME = ObjectCleaner.class.getSimpleName() + "Thread";
private static final Runnable CLEANER_TASK = new Runnable() { private static final Runnable CLEANER_TASK = new Runnable() {
@Override @Override
public void run() { public void run() {
@ -116,9 +116,9 @@ public final class ObjectCleaner {
}); });
cleanupThread.setName(CLEANER_THREAD_NAME); cleanupThread.setName(CLEANER_THREAD_NAME);
// This Thread is not a daemon as it will die once all references to the registered Objects will go away // Mark this as a daemon thread to ensure that we the JVM can exit if this is the only thread that is
// and its important to always invoke all cleanup tasks as these may free up direct memory etc. // running.
cleanupThread.setDaemon(false); cleanupThread.setDaemon(true);
cleanupThread.start(); cleanupThread.start();
} }
} }

View File

@ -23,6 +23,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
public class ObjectCleanerTest { public class ObjectCleanerTest {
@ -110,4 +112,26 @@ public class ObjectCleanerTest {
Thread.sleep(100); Thread.sleep(100);
} }
} }
@Test(timeout = 5000)
public void testCleanerThreadIsDaemon() throws Exception {
temporaryObject = new Object();
ObjectCleaner.register(temporaryObject, new Runnable() {
@Override
public void run() {
// NOOP
}
});
Thread cleanerThread = null;
for (Thread thread : Thread.getAllStackTraces().keySet()) {
if (thread.getName().equals(ObjectCleaner.CLEANER_THREAD_NAME)) {
cleanerThread = thread;
break;
}
}
assertNotNull(cleanerThread);
assertTrue(cleanerThread.isDaemon());
}
} }