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

时间:2014-12-12 19:48:41

标签: java groovy permgen

我有一个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();

我不知道如何修复此问题,因为我需要创建一个新实例才能运行该脚本。

2 个答案:

答案 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文件并将其包含在项目中来解决这个问题。