我有一个Java 7项目,每n分钟由n个进程运行脚本。以下是运行脚本的代码示例。
ScheduledFuture scheduledFuture = scheduledService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try (GroovyClassLoader cl = new GroovyClassLoader()) {
// Load up reusable script modules in the class loader
Class scriptClass = cl.parseClass(scriptSource);
Foo script = optimizationClass.newInstance();
// Tell Groovy that we don't need class meta info
GroovySystem.getMetaClassRegistry().removeMetaClass(scriptClass);
script.run();
cl.clearCache();
} catch (IOException e) {
LOGGER.error("Failed to cleanup Groovy class loader, this will cause a memory leak", e);
}
}
}, 0, scheduledRun, TimeUnit.SECONDS);
scheduledFuture.get();
由于某些原因,使用Groovy 2.1.7,Perm Gen中没有内存泄漏。升级到Groovy 2.3.8或Groovy 2.2.0时,Perm Gen不断填充死Groovy类加载器。
0x000000071ada2cd0 33 488160 0x000000071b2493c8 dead groovy / lang / GroovyClassLoader $ InnerLoader @ 0x00000007c7b70ef8 0x00000007265883b8 33 488160 0x0000000725837270 dead groovy / lang / GroovyClassLoader $ InnerLoader @ 0x00000007c7b70ef8 0x00000007157b5da0 26 370736 0x000000072326f468 live org / codehaus / groovy / runtime / callsite / CallSiteClassLoader @ 0x00000007c831d388 0x000000071ada1fb0 32 423944 0x000000071af03a98 dead groovy / lang / GroovyClassLoader $ InnerLoader @ 0x00000007c7b70ef8 0x0000000719d605b0 32 456520 0x000000071af04798 dead groovy / lang / GroovyClassLoader $ InnerLoader @ 0x00000007c7b70ef8 0x0000000725b82500 0 0 0x000000072326f468 dead groovy / lang / GroovyClassLoader @ 0x00000007c74c33e8 0x00000007263eef80 34 532448 0x0000000726d5c678 dead groovy / lang / GroovyClassLoader $ InnerLoader @ 0x00000007c7b70ef8 0x000000072687b3c8 33 485288 0x0000000726c36340 dead groovy / lang / GroovyClassLoader $ InnerLoader @ 0x00000007c7b70ef8 0x0000000725d56db0 33 485288 0x000000072607bcc0 dead groovy / lang / GroovyClassLoader $ InnerLoader @ 0x00000007c7b70ef8
我等到Full GC发生,但似乎Groovy 2.2之后的任何版本都导致Perm Gen填满。我检查了版本之间的版本说明,直到更新版本,我没有注意到会触发这个版本的任何更改。
我在这里检查了类似的问题并尝试了一些建议,但没有运气。关于原因的任何想法?
更新:
我在GroovyClassLoader上从2.1.7到2.2.0做了一个关于GrepCode的Diff,并且没有任何变化。我还在应用程序运行时获取了一个堆转储文件,并且没有任何GC根路径用于强引用。
问题似乎就在这里:
Class scriptClass = cl.parseClass(scriptSource);
Foo script = scriptClass.newInstance();
当我不编译脚本时,我在Perm Gen中获得了0个Groovy ClassLoader。当我编译脚本但不运行它时,我得到了死的Groovy ClassLoaders。
更新:
找到导致泄漏的代码。
Foo script = scriptClass.newInstance();
我不知道如何修复此问题,因为我需要创建一个新实例才能运行该脚本。
答案 0 :(得分:4)
使用groovy脚本进行编译和运行时遇到了同样的问题。 我终于以这种方式解决了问题: 1.如果你使用7以下的java版本,你可以使用下面的代码在编译后清理你的类
public static void clearAllClassInfo(Class<?> type) throws Exception {
Field globalClassValue = ClassInfo.class.getDeclaredField("globalClassValue");
globalClassValue.setAccessible(true);
GroovyClassValue classValueBean = (GroovyClassValue) globalClassValue.get(null);
classValueBean.remove(type);
}
2。否则你很幸运,因为你只需要在SystemProperties中添加一个属性
-Dgroovy.use.classvalue=true
答案 1 :(得分:0)
我们遇到类似问题的文件解析器用groovy编写,内存泄漏就像一个seave - 当你做很多字符串操作时。我们还尝试了类加载器上的clearCache()和.removeMetaClass(),没有用。
我们最终通过将groovy模块编译为jar文件并将其包含在项目中来解决这个问题。