Groovy更新导致PermGen中大量死亡的GroovyClassLoaders

我有一个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死常规/郎/ GroovyClassLoader $ InnerLoader @ 0x00000007c7b70ef8 0x00000007265883b8 33 488160 0x0000000725837270死常规/郎/ GroovyClassLoader $ InnerLoader @ 0x00000007c7b70ef8 0x00000007157b5da0 26 370736 0x000000072326f468活组织/ Codehaus的/常规/运行/调用点/ CallSiteClassLoader @ 0x00000007c831d388 0x000000071ada1fb0 32 423944 0x000000071af03a98死常规/郎/ GroovyClassLoader $ InnerLoader @ 0x00000007c7b70ef8 0x0000000719d605b0 32 456520 0x000000071af04798死常规/郎/ GroovyClassLoader $ InnerLoader @ 0x00000007c7b70ef8 0x0000000725b82500 0 0 0x000000072326f468死常规/郎/ GroovyClassLoader @ 0x00000007c74c33e8 0x00000007263eef80 34 532448 0x0000000726d5c678死常规/郎/ GroovyClassLoader $ InnerLoader @ 0x00000007c7b70ef8 0x000000072687b3c8 33 485288 0x0000000726c36340 dead groovy / lang / GroovyClassLoader $ InnerLoader @ 0x00000007c7b70ef8 0x0000000725d56db0 33 485288 0x000000072607bcc0 dead groovy / lang / GroovyClassLoader $ InnerLoa DER @ 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 ClassLoaders。当我编译脚本但不运行它时,我得到了死的Groovy ClassLoaders。

更新:
找到导致泄漏的代码。

 Foo script = scriptClass.newInstance(); 

不知道如何解决这个问题,因为我需要创建一个新实例才能运行脚本。

当使用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 

我们遇到类似的问题,文件解析器用groovy编写,内存泄漏就像一个seave – 当你做很多String操作时。 我们还尝试了类加载器上的clearCache()和.removeMetaClass(),没有用。

我们最终通过将groovy模块编译为jar文件并将其包含在项目中来解决这个问题。