首页 文章

Java / Groovy - GroovyClassLoader中的内存泄漏

提问于
浏览
8

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

您需要了解一些事项:

  • 每次运行脚本时我都要重新创建一个新的 GroovyScriptEngineImpl

  • 每次运行脚本时我都要重新创建一个新的 GroovyClassLoader

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

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

这导致加载的类数量无限增加,并且内存最终被完全填充 .

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

  • 在JVM参数中添加 -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC

  • 在JVM参数中添加 -Dgroovy.use.classvalue=true

  • 删除为每个 ScriptXXXX 类创建的"meta classes",如下所示:Groovy Classes not being collected but not signs of memory leak

  • 清除缓存并关闭 GroovyClassLoader

  • 使用内省手动清除某些缓存 GroovyScriptEngineImpl 中类的字段

  • 等......

这是Eclipse Memory Analyzer中 ScriptXXXX 类之一的"Shortest path to GC":

Eclipse Memory Analyzer dump

我在这里显然没有解决方案,似乎没有一个真正起作用,因为类加载器总是保持对从未获得GCed的类的引用 .

如果您想重现该问题,请参阅以下代码示例:

GroovyScriptEngineImpl se;

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

谢谢

UPDATE :阅读了pczeus 's reply, I tried limiting the metaspace, and some classes seem to be unloading indeed, and I think that it'的 ScriptXXX 类 .

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

这是我用VisualVM获得的配置文件:

Profile in the Java VisualVM

Eclipse内存分析器中用于 ScriptXXX 类的"Path to GC"确实是空的(它们不再是类的实例),即使该类仍然在直方图中列出 .

1 回答

  • 1

    这肯定似乎是一种边缘情况,需要特殊调整并可能选择使用的垃圾收集方案 .

    由于您使用的是Java8,并且您知道正在加载数千个临时类,因此您应该尝试调整并限制可用的数量并调整清理频率 . 直接https://blogs.oracle.com/poonam/entry/about_g1_garbage_collector_permanent

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

相关问题