我正在使用jvisualvm
检查应用程序中的内存泄漏。当我进行堆转储时,有时会有几个对象被打开,应该被垃圾收集。
当我对它们执行“显示最近的GC根”命令时,它向我显示根是我定义的类,它实现了Runnable接口。该引用列为(java frame)
,我知道它与线程有关。当我展开此节点的树时,它会打开并显示<no references>
。所以很明显,这不是我保持开放的参考,而是Java内部的东西。
jvisualvm中列出的GC Root对象的类型为AnalyticNode extends Node
,而Node implements Runnable
为{{1}}。尽管使用了“frame”一词,但这个根对象绝不与AWT,Swing或任何重量级用户界面组件有任何关系。在这种情况下,单词“frame”指的是线程。
Java是否会保留对最后一个Runnable的引用?有什么方法可以告诉Java发布这个引用,以便可以正确地为我的堆转储收集垃圾?
这里发生了什么?
答案 0 :(得分:4)
在这种情况下,&#34;框架&#34;指的是堆栈框架。听起来像Runnable
,而不是(或除此之外)是正在运行的线程的目标,它存储在执行线程的堆栈中的帧中的局部变量中。当与帧相关联的方法返回时,它将有资格进行收集。
根据后续评论,我的猜测是,在您的自定义线程池中,有一个Runnable
被分配到的局部变量。它可能在一个范围太大(在循环之外)并且在循环的每次迭代之后没有被清除(分配null
)。
我可以在工作线程中重现与这样的代码描述匹配的情况:
Runnable target = null;
while (true) {
target = queue.take();
target.run();
}
清理target
的声明以使其处于循环内部可以解决问题。
我建议从核心Java切换到Executor
实现,或者如果要修复它,可以发布自定义线程池的相关代码。
答案 1 :(得分:1)
您对自己创建的对象做了什么?你创建了一个线程并指向它吗?在这种情况下,您必须通过允许run()中的代码完成运行来确保线程已停止。