微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

java – 即使没有GC根,Classloader也不会被垃圾收集

我们有一个在Glassfish V2.1.1下运行的复杂应用程序.为了能够动态加载我们的代码,我们实现了一个CustomClassloader,它能够重新定义类.
行为非常简单:当动态加载的类发生更改时,CustomClassloader的当前实例将被“删除”,并创建一个新实例以重新定义所需的类.

这很好用,除了在重新加载相同的类几次之后(因此每次创建一个新的CustomClassloader),我们得到一个PermGen空间错误,因为CustomClassloader的其他实例不是垃圾收集的. (这个类应该只有一个实例)

我尝试了不同的方法来追踪泄漏的位置:

> visualvm =>我进行堆转储并提取CustomClassloader的所有实例.我可以看到他们都没有最终确定.当我检查最近的GC根时,visualvm告诉我没有(除了最后一个实例,因为它是’真实的’使用过的).
> jmap/jhat =>它给了我几乎相同的结果:我看到CustomClassloader的所有实例,然后当我点击链接以查看其中一个的引用位置时,我得到一个空白页面意味着没有…
> Eclipse Memory Analyzer Tool =>当我运行以下OQL查询时,我得到一个奇怪的结果:
选择c FROM INSTANCEOF my.package.CustomClassloader c
只有一个结果,表明只有一个实例显然不正确.

我还检查了这个link,并在创建一个新的CustomClassloader时实现了一些资源释放,但没有任何变化:PermGen内存仍在增加.

所以我可能错过了一些东西,点(1-2)和(3)之间的区别显示了我不理解的东西.我在哪里可以了解什么是错的?
由于我所遵循的所有教程都显示了如何使用“搜索最近的GC根”功能搜索泄漏的引用(在我的情况下没有),我不知道如何跟踪错误.

编辑1:我上传一个堆转储here的示例.可以使用以下查询在visualvm中选择未卸载的ClassLoader:select s from saierp.core.framework.system.SAITaskClassLoader s
可以看到有4个实例,并且应该已经收集了第3个实例,因为没有GC根…必须有某个引用,但我不知道如何搜索它.任何提示都欢迎:)

编辑2:经过一些更深入的测试,我看到一个非常奇怪的模式.泄漏似乎取决于OpenJPA正在加载的数据:如果没有加载新数据,那么类加载器可以是GCed,否则不是.下面是我创建一个新的SAITaskClassLoader以“清除”旧代码时使用的代码

PCRegistry.deRegister(cl);
LogFactory.release(cl);
ResourceBundle.clearCache(cl);
Introspector.flushCaches();

=模式1(类加载器已GCed):=

>新的SAITaskClassLoader
>加载数据D1,D2,…,Dn
>新的SAITaskClassLoader
>加载数据D1,Dn
> ……

=模式2(类加载器未GCed):=

>新的SAITaskClassLoader
>加载数据D1,D3
>新的SAITaskClassLoader
>加载数据D3,D4,D5
>新的SAITaskClassLoader
>加载数据D5,D6,D7
> ……

在所有情况下,已清除的SAITaskClassLoader都没有GC根.我们正在使用OpenJPA 1.2.1.

谢谢&最好的祝福

解决方法

如果没有CustomClassLoader源代码的片段或实际的堆转储,就很难找到问题所在.您的CustomClassLoader不能是单例.如果是,你的设计无法工作(或者我错过了什么).

您需要获取CustomClassLoader类型的ClassLoader实例列表,并跟踪对这些对象的引用.

这些帖子可以帮助您进一步分析它,并进入暗中追捕ClassLoader泄漏的暗示:

> Debugging Dynamic ClassLoaders in Heap Dumps
> How to analyze leaky web-apps
> A couple of typical causes of ClassLoader leaks

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐