Details
-
Bug
-
Status: Closed
-
Major
-
Resolution: Fixed
-
2.4.7
-
None
-
None
-
Oracle JDK6, Oracle JDK7
Description
The following code causes a memory leak in perm gen reproducibly on Oracle JDK6 and 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(); Script script = gs.parse(" 'Hello, World';"); Object result = script.run(); assert result.equals("Hello, World"); } } }
Tested with the following VM options (with PermGen size 20MB), the execution results in java.lang.OutOfMemoryError: PermGen space after a little more than 2000 iterations.
-verbose:class -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+CMSClassUnloadingEnabled -XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses -XX:+TraceClassUnloading -XX:MaxPermSize=16M -XX:+UseConcMarkSweepGC -XX:+ExplicitGCInvokesConcurrent -XX:+PrintGCDetails -XX:-UseParNewGC
As a workaround, calling the following method with script.getClass() from above (as described in http://stackoverflow.com/a/41473210/2156613) seems to avoid this leak and trigger class unloading correctly:
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); } }
This should be handled by Groovy itself, or (if this is not possible) there should be at least a possibility to trigger this functionality via a Groovy API call instead of having to create the above method in all applications using Groovy.
I can replicate the error with 2.4.7 but not with 2.4.8-SNAPSHOT. Possibly fixed by
GROOVY-7683. As a work-around for 2.4.7 you might want to try -Dgroovy.use.classvalue=true.