Classloader由弱引用持有?

时间:2013-12-02 20:27:11

标签: java memory-leaks garbage-collection classloader

我一直在与一些内存泄漏作斗争,我目前对这个问题感到困惑。有一个Web应用程序类加载器,应该是垃圾收集,但它不是(即使我修复了几个泄漏)。我用jmap转储堆并用jhat浏览它,找到了类加载器并检查了rootset引用。

如果我排除弱参考,列表为空!这怎么可能,因为只有弱引用持有的对象应该收集垃圾? (我在jconsole中多次执行GC)

如果我包含弱引用,我会得到一个引用列表,所有引用都来自以下某个字段:

  • java.lang.reflect.Proxy.loaderToCache
  • java.lang.reflect.Proxy.proxyClasses
  • java.io.ObjectStreamClass中$ Caches.localDescs
  • java.io.ObjectStreamClass中$ Caches.reflectors
  • java.lang.ref.Finalizer.unfinalized

我找不到任何理由为什么这些引用应该防止垃圾收集类加载器。这是一个gc bug吗?特殊无证案件? jmap / jhat bug?或者是什么?

最奇怪的事情......在闲置和闲逛之后大约40分钟,没有改变任何东西,它最终决定卸载课程并收集课程装载。

注意:

如果您对延迟收集的类加载器或弱引用提出索赔,请说明发生的情况,理想情况下:

  • 提供指向支持您声明的权威文章的链接
  • 提供演示行为的示例程序

如果您认为该行为是依赖于实现的,那么请关注oracle或icedtea jvm版本6或7中发生的事情(选择其中任何一个并具体说明)。

我真的想深究这一点。我实际上已经花了一些力气来重现测试程序中的问题,但我失败了 - 除非有强烈的引用,否则每次都会立即在System.gc()上收集类加载器。

2 个答案:

答案 0 :(得分:3)

看起来某处有一个软参考。这是我能找到的延迟收集的唯一解释(约40分钟)。我最初认为软件引用一直保留到内存耗尽,但我发现事实并非如此。

来自this page:“软件可访问的对象在上次引用后会保持活动一段时间。默认值是堆中每个自由兆字节的生命周期的一秒。此值可以调整使用-XX:SoftRefLRUPolicyMSPerMB标志“

所以我将该标志调整为1,并在几秒钟内收集了类加载器!!

我认为软引用来自ObjectStreamClass。问题是为什么jhat没有在rootset引用中显示它。是因为它既不强壮也不弱?或者因为它已经从相同的静态字段中找到了弱引用?还是其他一些原因?无论哪种方式,我认为这需要在jhat中进行改进。

答案 1 :(得分:2)

类驻留在特殊的内存空间中 - 永久生成。卸载类加载器。 GC应选择将烫发空间纳入收集范围。不同的GC算法有一些不同的行为,但通常GC会尽量避免烫发空间收集。

根据我的经验,即使无法访问类加载器,JVM也可能会在尝试收集PERM空间之前使用OutOfMemoryError。