我正在加载大量Groovy(2.4.6)脚本并在我的Java 8应用程序中使用GroovyScriptEngineImpl运行它们,并且我在一段时间后遇到了问题。
您需要了解一些事项:
GroovyScriptEngineImpl
GroovyClassLoader
我需要这样做,以便在单独的环境中隔离每个脚本":我在类加载器中加载一些外部JAR以获取某些脚本,我不会这样做希望其他脚本能够在这些JAR执行时使用这些类。
我的问题来自这样一个事实:对于我运行的每个脚本,GroovyClassLoader
将创建一个新的ScriptXXXX
类并加载它,但永远不会卸载它。
这导致加载的类数量无限增加,内存最终被完全填充。
我尝试了大量的各种解决方案,但似乎都没有:
-XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC
-Dgroovy.use.classvalue=true
ScriptXXXX
课程创建,如下所示:Groovy Classes not being collected but not signs of memory leak GroovyClassLoader
GroovyScriptEngineImpl
这是" GC的最短路径"对于Eclipse Memory Analyzer中的ScriptXXXX
类之一:
我在这里显然没有解决方案,似乎没有一个真正起作用,因为类加载器总是保留对从未获得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获得的个人资料:
" GC的路径"在Eclipse内存中,Out of Metaspace
类的内存分析器确实是空的(它们不再是类的实例),即使该类仍然在直方图中列出。
答案 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设置为更高的值以延迟诱导的垃圾收集。在诱导垃圾收集之后,可能会增加引发下一次垃圾收集所需的类元数据使用。