executor 应该发生什么(它是死线程的本地,没有外部引用)?是否应该被GCed?
答案比“是的,如果没有引用它就会是”更复杂。这取决于ThreadPoolExecutor 中运行的线程是否仍在运行。这又取决于创建的 TPE 类型以及提交给它的“长时间运行的任务”是否已完成。
例如,如果任务尚未完成,那么线程仍将运行。
即使他们已经完成,如果您的 TPE 的核心线程没有设置allowCoreThreadTimeOut(true),那么线程也不会停止。 JVM 从不垃圾收集正在运行的线程,因为它们被认为是GC "roots":
...根据定义,正在运行的线程不受 GC 的影响。 GC 通过扫描“根”开始其工作,这些“根”被认为始终可以访问;根包括全局变量(Java-talk 中的“静态字段”)和所有正在运行的线程的堆栈...
所以下一个问题是线程是否有对ThreadPoolExecutor 的引用返回,我相信他们有。 Worker 内部类是存储在 thread.target 中的 Runnable 并由 Thread 执行,因此它不能被 GC 处理。 Worker 不是 static,因此它隐含了对外部 ThreadPoolExecutor 实例的引用。 run() 方法实际上调用了ThreadPoolExecutor.runWorker() 方法,该方法引用了ThreadPoolExecutor 管理的所有任务队列。因此,正在运行的线程保留对 Worker 和 TPE 的引用,因此垃圾收集器无法收集 TPE。
例如,这是一个引用 TPE 的运行池线程的典型堆栈帧:
java.lang.Thread.sleep(Native Method)
com.j256.GcTester$1.run(GcTesteri.java:15)
java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
java.util.concurrent.FutureTask.run(FutureTask.java:266)
>> java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
>> java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
java.lang.Thread.run(Thread.java:748)
但是,如果线程池任务已全部完成并且它有 0 个核心线程或核心线程已超时,那么将没有与 ThreadPoolExecutor 关联的 Worker 线程。然后 TPE 将被垃圾回收,因为除了循环引用之外没有对它的引用,GC 足够聪明地检测到。
这是一个演示它的小示例测试程序。如果有 1 个核心线程,则 TPE 将永远不会关闭(通过finalize()),即使在注意到/tmp/x 文件存在后工作线程退出后也是如此。即使主线程没有引用它也是如此。但是,如果有 0 个核心线程,那么在线程超时后(这里是在完成最后一个任务后 1 秒后)将收集 TPE。
public class GcTester {
public static void main(String[] args) {
int numCore = 1; // set to 0 to have it GC'd once /tmp/x file exists
ExecutorService pool =
new ThreadPoolExecutor(numCore, Integer.MAX_VALUE,
1, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()) {
protected void terminated() {
System.out.println(this + " terminated");
}
};
pool.submit(new Runnable() {
public void run() {
while (true) {
Thread.sleep(100); // need to handle exception here
if (new File("/tmp/x").exists()) {
System.out.println("thread exiting");
return;
}
}
}
});
pool = null; // allows it to be gc-able
while (true) {
Thread.sleep(1000); // need to handle exception here
System.gc(); // pull the big GC handle
}
}
}