我一直在处理应用程序中的类加载器泄漏,最终达到了对CL的所有引用都消失的程度。在我的内存分析工具(使用YourKit和jmap / jhat)中,我可以强制GC有时会立即摆脱我的类加载器,但是其他时候(取决于应用程序使用情况,但这是我可以获得的具体情况)当我强制GC,CL实例不会消失。我捕获一个内存快照并查看结果,它说该对象存在,但无法访问。
这是古怪的部分......我偶然发现了这一点。
我可以强制使用我想要的全部GC,但实例并没有消失。然而,在10-20分钟之后,如果我再做一次完整的GC, 会被收集。
(在此期间,应用程序大部分处于非活动状态(并非完全)。我的“延长”咖啡休息时间是导致此发现的事故。)
所以我对此处泄漏的担忧已经消失(希望如此),但问题现在更多是试图解释这种行为。
有谁知道什么可能导致Sun JVM决定不收集无法访问的类加载器20分钟?
Sun JVM版本详细信息:
java version "1.6.0_23"
Java(TM) SE Runtime Environment (build 1.6.0_23-b05)
Java HotSpot(TM) 64-Bit Server VM (build 19.0-b09, mixed mode)
答案 0 :(得分:5)
我认为Jeremy Heiler正处于正确的轨道上,因为它是一个定型问题。基本上,即使GC知道某个对象无法访问,在实际收集对象之前它可能仍然是无限期的,因为它可能被排队以进行最终确定。终结器线程必须绕过最终化对象(可能需要一段时间,这取决于需要完成多少其他对象以及它们的终结器的外观)然后,在对象完成后,您将需要另一个 GC,用于在实际收集对象之前重新发现该对象无法访问。
另外,对于Sun JVM上的ClassLoader,它可能直接分配给PermGen。所以你不需要一个,而是两个完整的PermGen扫描(一个用于将对象放入终结队列,然后是第二个用于实际收集它)。由于PermGen扫描很昂贵,我相信Sun JVM根本不会使用默认设置来执行它们,即使使用各种GC调整,它仍然非常不愿意扫描PermGen(特别是如果没有很多其他内存压力) )。
如果您使用System.gc()
代替强制(嗯,我的意思是,鼓励)GC,那么如果您执行以下操作会怎样:
System.gc();
System.runFinalization();
System.gc();
当然,即使这不能保证可行,但它至少可以完成清理大型终结所需对象所需的最少工作。
答案 1 :(得分:0)
可能因为JVM使用generational垃圾收集,但最终会进行完整的标记和扫描
答案 2 :(得分:0)
只是猜测:Classloader(以及它加载的类)不在常规堆中,而是在永久生成(“PermGen”)中。因为在正常情况下,类加载器不会超出范围,所以内存的这一部分将比其他所有内容更轻松地滑动。
(可能有一些GC配置选项可以影响这个频率。)我想填充PermGen也会在那里触发一个集合,但你通常不想这样做。
为什么你需要收集你的类加载器呢?
答案 3 :(得分:0)