重复使用Groovy Shell会导致PermGen空间已满

时间:2017-01-04 14:10:16

标签: java groovy garbage-collection permgen

在应用程序中,我重复创建GroovyShell实例并在其上执行自定义脚本。这样做时,我遇到的问题是Java 6和Java 7中的perm gen空间正在填满,并且没有卸载类。我正在使用Groovy 2.4.7。可以使用以下代码重现该问题:

import groovy.lang.GroovyShell;

public class Demo {


    public static void main(String[] args) throws Exception {
        for (int i = 0; i < 10000000; i++) {
            GroovyShell gs = new GroovyShell();
            Object result = gs.evaluate(" 'Hello, World';");
            assert result.equals("Hello, World");
        }
    }
}

我正在使用以下VM参数:

-verbose:class -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+CMSClassUnloadingEnabled -XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses -XX:+TraceClassUnloading -XX:MaxPermSize=16M -XX:+UseConcMarkSweepGC -XX:+ExplicitGCInvokesConcurrent -XX:+PrintGCDetails -XX:-UseParNewGC

当我从上面执行测试时,经过2000多次迭代后,PermGen已满。此外,手动触发GC也不起作用。

有没有办法强制类在这里卸载或触发GC以便从PermGen空间中删除类?

3 个答案:

答案 0 :(得分:2)

我能够感同身受,因为我已经和它搏斗了一段时间。 我实现了this solution,它改进了Java 1.6中PermGen的生命周期,但没有消除核心问题。

出于某种原因,我没有在Java 8中看到问题,所以我放弃了尝试解决。 (没试过Java 7)。

希望你能比我更进一步。

答案 1 :(得分:1)

哇。耽误。我刚给了this one一个快速的镜头,它看起来很完美。但是,不是直接使用shell,而是编译脚本然后运行脚本(对于相同的结果)。然后调用指定的清洁工。

public static void test() {
    for (int i = 0; i < 10000000; i++) {
        GroovyShell gs = new GroovyShell();
        Script script = gs.parse(" 'Hello, World';");
        Object result = script.run();
        assert result.equals("Hello, World");
        clearAllClassInfo(script.getClass());
    }                  
}

public static void clearAllClassInfo(Class<?> type) {
    try {
        Field globalClassValue = ClassInfo.class.getDeclaredField("globalClassValue");
        globalClassValue.setAccessible(true);
        GroovyClassValue classValueBean = (GroovyClassValue) globalClassValue.get(null);
        classValueBean.remove(type);
    } catch (Exception ex) {
        throw new RuntimeException(ex);
    }           
}      

显然,您可以优化清洁剂以消除一堆反射。

PermGen相当扁平: enter image description here

班级正以轻快的速度卸载: enter image description here

归功于@ BilboDai

答案 2 :(得分:0)

重用GroovyShell实例:

import groovy.lang.GroovyShell;

public class Demo {


    public static void main(String[] args) throws Exception {
        //Create outside the loop
        GroovyShell gs = new GroovyShell();
        for (int i = 0; i < 10000000; i++) {
            Object result = gs.evaluate(" 'Hello, World';");
            assert result.equals("Hello, World");
        }
    }
}