首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >GroovyClassLoader中的内存泄漏

GroovyClassLoader中的内存泄漏
EN

Stack Overflow用户
提问于 2016-05-18 13:29:23
回答 1查看 6.3K关注 0票数 11

我正在加载大量Groovy (2.4.6)脚本,并在我的Java8应用程序中使用GroovyScriptEngineImpl运行它们,过了一段时间,我遇到了一个问题。

有几件事你需要知道:

  • 每次运行脚本时,我必须重新创建一个新的GroovyScriptEngineImpl
  • 每次运行脚本时,我必须重新创建一个新的GroovyClassLoader

为了在一个单独的“环境”中隔离每个脚本,我需要这样做:我在类加载器中为某些脚本加载一些外部JAR,我不希望其他脚本在执行这些JAR时能够使用这些类。

我的问题来自这样一个事实:对于我运行的每一个脚本,GroovyClassLoader都会创建一个新的ScriptXXXX类并加载它,但是永远不会卸载它。

这会导致加载的类数量不确定地增加,并且内存最终完全被填满。

我尝试了大量的各种解决方案,但似乎都没有效果:

  • 在JVM参数中添加-XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC
  • 在JVM参数中添加-Dgroovy.use.classvalue=true
  • 删除为每个ScriptXXXX类创建的“元类”,如下所示:Groovy类没有被收集,但没有内存泄漏的迹象
  • 清除缓存并关闭GroovyClassLoader
  • 使用内省手动清除GroovyScriptEngineImpl中缓存类的某些字段
  • 等等。

下面是Eclipse中的一个ScriptXXXX类的“到GC的最短路径”:

我在这里显然没有解决方案了,而且似乎没有一个真正起作用,因为类加载器总是保持对那些永远得不到GCed的类的引用。

如果您想重现这个问题,下面是一个代码示例:

代码语言:javascript
复制
GroovyScriptEngineImpl se;

while (true)
{
    se = new GroovyScriptEngineImpl(new GroovyClassLoader());
    CompiledScript script = se.compile("println(\"hello\")");
    script.eval(se.createBindings());
}

谢谢

UPDATE:在阅读了pczeus的答复之后,我尝试限制metaspace,有些类似乎确实在卸载,我认为是ScriptXXX类。

也就是说,几分钟后,我将在脚本执行过程中得到Out of Metaspace错误。

下面是我在VisualVM上得到的个人资料:

ScriptXXX类的Eclipse中的“到GC的路径”确实是空的(它们不再是类的实例),甚至类仍然列在直方图中。

EN

回答 1

Stack Overflow用户

发布于 2016-05-22 17:10:35

这显然是一种边缘情况,需要进行特殊的调优,并可能选择使用的垃圾收集方案。

由于您使用的是Java8,而且您知道您正在加载数千个临时类,所以您应该尝试优化和限制可用的MetaSpace数量,并优化清理的频率。直接从永久的

JDK8: Metaspace 在JDK 8中,类元数据现在存储在本机堆中,这个空间称为Metaspace。JDK 8中为Metaspace添加了一些新标志: -XX:MetaspaceSize=,其中是分配给类元数据(以字节为单位)的初始空间量(初始高水标记),这些空间可能导致垃圾收集卸载类。这个数额是近似的。在首次到达高水标记之后,下一个高水标记将由垃圾收集器管理。 -XX:MaxMetaspaceSize=,其中是为类元数据分配的最大空间(以字节为单位)。此标志可用于限制为类元数据分配的空间。这个值是近似的。默认情况下没有设置限制。-XX:MinMetaspaceFreeRatio=是在GC之后释放类元数据容量的最小百分比,以避免为类元数据分配的空间(高水标记)增加,从而导致垃圾收集。 -XX:MaxMetaspaceFreeRatio=是在GC之后释放类元数据容量的最大百分比,以避免减少分配给类元数据的空间(高水标记),从而导致垃圾收集。 默认情况下,类元数据分配仅受可用本机内存量的限制。我们可以使用新的选项MaxMetaspaceSize来限制用于类元数据的本机内存量。它类似于MaxPermSize。当类元数据使用达到MetaspaceSize时(32位客户端VM上的12兆字节和64位VM上较大大小的32位服务器VM上的16兆字节),就会引入垃圾收集来收集死类加载器和类。将MetaspaceSize设置为更高的值,以延迟诱导的垃圾回收。在诱导垃圾收集之后,诱导下一个垃圾收集所需的类元数据使用量可能会增加。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/37301117

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档