gpt4 book ai didi

Java/Groovy - GroovyClassLoader 中的内存泄漏

转载 作者:塔克拉玛干 更新时间:2023-11-03 03:24:56 51 4
gpt4 key购买 nike

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

有几件事你需要知道:

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

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

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

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

我尝试了很多不同的解决方案,但似乎都没有用:

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

这是 ScriptXXXX 之一的“到 GC 的最短路径” Eclipse 内存分析器中的类:

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的回复后,我尝试限制元空间,有些类似乎确实在卸载,我认为这是ScriptXXX类。

就是说,几分钟后我得到了 Out of Metaspace脚本执行期间出错。

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

Profile in the Java VisualVM

以及 ScriptXXX 的 Eclipse 内存分析器中的“GC 路径”类确实是空的(它们不再是类的实例),即使该类仍列在直方图中。

最佳答案

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

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

JDK8: Metaspace

In JDK 8, classes metadata is now stored in the native heap and this space is called Metaspace. There are some new flags added for Metaspace in JDK 8:

-XX:MetaspaceSize= where is the initial amount of space(the initial high-water-mark) allocated for class metadata (in bytes) that may induce a garbage collection to unload classes. The amount is approximate. After the high-water-mark is first reached, the next high-water-mark is managed by the garbage collector

-XX:MaxMetaspaceSize= where is the maximum amount of space to be allocated for class metadata (in bytes). This flag can be used to limit the amount of space allocated for class metadata. This value is approximate. By default there is no limit set. -XX:MinMetaspaceFreeRatio= where is the minimum percentage of class metadata capacity free after a GC to avoid an increase in the amount of space (high-water-mark) allocated for class metadata that will induce a garbage collection.

-XX:MaxMetaspaceFreeRatio= where is the maximum percentage of class metadata capacity free after a GC to avoid a reduction in the amount of space (high-water-mark) allocated for class metadata that will induce a garbage collection.

By default class metadata allocation is only limited by the amount of available native memory. We can use the new option MaxMetaspaceSize to limit the amount of native memory used for the class metadata. It is analogous to MaxPermSize. A garbage collection is induced to collect the dead classloaders and classes when the class metadata usage reaches MetaspaceSize (12Mbytes on the 32bit client VM and 16Mbytes on the 32bit server VM with larger sizes on the 64bit VMs). Set MetaspaceSize to a higher value to delay the induced garbage collections. After an induced garbage collection, the class metadata usage needed to induce the next garbage collection may be increased.

关于Java/Groovy - GroovyClassLoader 中的内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37301117/

51 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com