在应用程序中,我重复创建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空间中删除类?
答案 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);
}
}
显然,您可以优化清洁剂以消除一堆反射。
归功于@ 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");
}
}
}