我正在加载大量的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":
我在这里显然没有解决方案,似乎没有一个真正起作用,因为类加载器总是保持对从未获得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获得的配置文件:
Eclipse内存分析器中用于 ScriptXXX
类的"Path to GC"确实是空的(它们不再是类的实例),即使该类仍然在直方图中列出 .
1 回答
这肯定似乎是一种边缘情况,需要特殊调整并可能选择使用的垃圾收集方案 .
由于您使用的是Java8,并且您知道正在加载数千个临时类,因此您应该尝试调整并限制可用的数量并调整清理频率 . 直接https://blogs.oracle.com/poonam/entry/about_g1_garbage_collector_permanent: