Java / Groovy - GroovyClassLoader中的内存泄漏

时间:2016-05-18 13:29:24

标签: java groovy memory-leaks classloader

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

您需要了解一些事项:

  • 我每次运行脚本时都必须重新创建GroovyScriptEngineImpl
  • 我每次运行脚本时都必须重新创建GroovyClassLoader

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

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

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

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

  • 在JVM参数中添加-XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC
  • 在JVM参数中添加-Dgroovy.use.classvalue=true
  • 删除"元类"为每个ScriptXXXX课程创建,如下所示:Groovy Classes not being collected but not signs of memory leak
  • 清除缓存并关闭GroovyClassLoader
  • 使用内省手动清除缓存GroovyScriptEngineImpl
  • 中的类的某些字段
  • 等...

这是" GC的最短路径"对于Eclipse Memory Analyzer中的ScriptXXXX类之一:

Eclipse Memory Analyzer dump

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

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

GroovyScriptEngineImpl se;

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

由于

更新:在阅读了pczeus的回复后,我尝试限制元空间,有些类似乎确实在卸载,我认为它是{{1} } class。

也就是说,几分钟后我在脚本执行过程中出现ScriptXXX错误。

以下是我使用VisualVM获得的个人资料:

Profile in the Java VisualVM

" GC的路径"在Eclipse内存中,Out of Metaspace类的内存分析器确实是空的(它们不再是类的实例),即使该类仍然在直方图中列出。

1 个答案:

答案 0 :(得分:2)

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

由于您使用的是Java8,并且您知道正在加载数千个临时类,因此您应该尝试调整并限制可用的SSLSocket数量并调整清理频率。直接从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设置为更高的值以延迟诱导的垃圾收集。在诱导垃圾收集之后,可能会增加引发下一次垃圾收集所需的类元数据使用。