diff --git a/common/src/main/java/io/netty/util/internal/ObjectCleaner.java b/common/src/main/java/io/netty/util/internal/ObjectCleaner.java index 96d0443956..991f30d0dc 100644 --- a/common/src/main/java/io/netty/util/internal/ObjectCleaner.java +++ b/common/src/main/java/io/netty/util/internal/ObjectCleaner.java @@ -16,8 +16,6 @@ package io.netty.util.internal; 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.WeakReference; @@ -36,11 +34,13 @@ import static java.lang.Math.max; public final class ObjectCleaner { private static final int REFERENCE_QUEUE_POLL_TIMEOUT_MS = 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() private static final Set LIVE_SET = new ConcurrentSet(); private static final ReferenceQueue REFERENCE_QUEUE = new ReferenceQueue(); 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() { @Override public void run() { @@ -116,9 +116,9 @@ public final class ObjectCleaner { }); 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 - // and its important to always invoke all cleanup tasks as these may free up direct memory etc. - cleanupThread.setDaemon(false); + // Mark this as a daemon thread to ensure that we the JVM can exit if this is the only thread that is + // running. + cleanupThread.setDaemon(true); cleanupThread.start(); } } diff --git a/common/src/test/java/io/netty/util/internal/ObjectCleanerTest.java b/common/src/test/java/io/netty/util/internal/ObjectCleanerTest.java index 0db3e26945..7e75504021 100644 --- a/common/src/test/java/io/netty/util/internal/ObjectCleanerTest.java +++ b/common/src/test/java/io/netty/util/internal/ObjectCleanerTest.java @@ -23,6 +23,8 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; public class ObjectCleanerTest { @@ -110,4 +112,26 @@ public class ObjectCleanerTest { 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()); + } }