请帮我在下面的代码中找到线程泄漏的原因。即使在run()完成后(从安慰的print语句中验证)并且main方法已退出(从print statement和profiler工具验证),TestThread
也不会收集垃圾。
然而,TestThread
如果被设置为守护程序线程,即t.setDaemon(true)
,则会收集垃圾。下面的代码只是一个示例代码,用于说明我的应用程序中的问题。我正在尝试使用一些预先存在的调度类(由其他人使用ScheduledExecutorService
设计)。我注意到,当我继续使用类调度多个Runnable
时,创建的线程永远不会被垃圾收集。
public class ThreadTest {
static void runThreadWithExecutor() {
final String name = "TestThread";
ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor(
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, name);
t.setDaemon(false);
return t;
}
});
ses.schedule(new Runnable() {
@Override
public void run() {
System.out.println("entered " + name);
System.out.println("exiting " + name);
}},
2,
TimeUnit.SECONDS);
}
public static void main(String[] args) throws InterruptedException {
System.out.println("entered main");
runThreadWithExecutor();
Thread.sleep(5000);
System.out.println("exiting main");
}
}
答案 0 :(得分:5)
这是因为您在安排上一份工作后没有在执行人服务上调用shutdown()
:
ses.schedule(...);
// this stops any management threads but existing jobs will still run
ses.shutdown();
我刚刚将shutdown()
调用添加到您的代码中,并且退出正常。所有ExecutorService
都是如此。如果没有关闭,线程池将继续等待提交更多作业,并且永远不会GC。
有关详细信息,请参阅@ John的答案。
答案 1 :(得分:3)
@Gray对他的评估是正确的我只是想我添加他为什么是正确的。 ExecutorService是一个将重用线程的线程池。
与new Thread(runnable).start();
不同,当run方法完成时,线程完成,然后将进行GC。当Executor Runnable完成时,线程将坐在那里等待另一个可运行的任务被提交和使用。因此,通过关闭,您将告诉执行程序结束线程池中的所有线程。
回答你的最后一部分。将其设置为守护程序只能运行,因为没有其他(非守护程序)线程在运行。如果您的应用程序启动了一些其他非守护程序线程,则Executor线程将继续。请记住,只有守护程序线程运行时才会终止守护程序线程。