You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@groovy.apache.org by "Paul King (JIRA)" <ji...@apache.org> on 2017/02/01 23:19:03 UTC
[jira] [Closed] (GROOVY-7109) Severe Memory Leak Happens when
Script Executes
[ https://issues.apache.org/jira/browse/GROOVY-7109?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Paul King closed GROOVY-7109.
-----------------------------
> Severe Memory Leak Happens when Script Executes
> -----------------------------------------------
>
> Key: GROOVY-7109
> URL: https://issues.apache.org/jira/browse/GROOVY-7109
> Project: Groovy
> Issue Type: Bug
> Components: groovy-runtime
> Affects Versions: 2.3.6
> Reporter: Troy Liu
> Assignee: John Wagenleitner
> Priority: Critical
> Fix For: 2.4.8
>
>
> Recently I found my website creates so many classes in permGen. In dump file I can see so many strange classes such as java_util_List$size, java_util_List$subList.
> I don't know what are these classes, so I just call them "method classes". The serious thing is that, each "method class" has many "anonymous classes", such as java_util_List$size$0, java_util_List$size$1, java_util_List$size$2 ... There are over 1,000 java_util_List$size, and over 60,000 class totally in permGen.
> Then I test the groovy script engine and find that when script is excuting, these strange method is created.
> Here is my test code.
> {code:title=TodoJava.java|borderStyle=solid}
> ScriptEngineManager m = new ScriptEngineManager();
> ScriptEngine engine = m.getEngineByName("groovy");
> CompiledScript script = ((Compilable) engine).compile(new FileReader( "test-script.groovy" ));
> {code}
> The test-script.groovy file:
> {code:title=test-script.groovy|borderStyle=solid}
> import cn.ming.youxi.TestClazz
> Random random = new Random();
> for (i in 1 .. 10) {
> random.nextInt(i);
> }
> TestClazz testClazz = clazz;
> List list = Arrays.asList(
> testClazz.att,
> testClazz.att,
> testClazz.att,
> testClazz.att,
> testClazz.att,
> testClazz.att
> );
> return list.get(2);
> {code}
> TestClazz is a simple class with an Integer attribute. When I call this method, the "method classes" is created.
> {code:title=TodoJava.java|borderStyle=solid}
> public void run(CompiledScript script) {
> TestClazz testClazz = new TestClazz(new Random().nextInt());
> Map<String, Object> params = new HashMap<String, Object>();
> params.put("clazz", testClazz);
> Bindings bindings = engine.createBindings();
> bindings.putAll(params);
> try {
> System.out.println(script.eval(bindings));
> } catch (ScriptException e) {
> e.printStackTrace();
> }
> }
> {code}
> But there is only one java_util_List$get and java_util_Random$nextInt no matter how many times I call it. Then I try to execute the script in muti-thread. Here's the code:
> {code:title=TodoJava.java|borderStyle=solid}
> static class MyThread extends Thread {
> ScriptEngine engine;
> CompiledScript script;
> public MyThread(ScriptEngine engine, CompiledScript script) {
> this.engine = engine;
> this.script = script;
> }
> public void run() {
> TestClazz testClazz = new TestClazz(new Random().nextInt());
> Map<String, Object> params = new HashMap<String, Object>();
> params.put("clazz", testClazz);
> Bindings bindings = engine.createBindings();
> bindings.putAll(params);
> try {
> System.out.println(script.eval(bindings));
> } catch (ScriptException e) {
> e.printStackTrace();
> }
> }
> }
> {code}
> And I make 10 thread every 50 ms.
> {code:title=TodoJava.java|borderStyle=solid}
> public static void main(String[] args) throws Exception {
> ScriptEngineManager m = new ScriptEngineManager();
> ScriptEngine engine = m.getEngineByName("groovy");
> CompiledScript script = ((Compilable) engine).compile(new FileReader( "test-script.groovy" ));
> InputStreamReader is_reader = new InputStreamReader(System.in);
> String str = new BufferedReader(is_reader).readLine();
> while (true) {
> for (int i = 0; i < 10; ++i) {
> MyThread myThread = new MyThread(engine, script);
> myThread.start();
> }
> Thread.sleep(50);
> }
> }
> {code}
> After a while, I see 90 java_util_List$iterator and 140 java_util_Random$nextInt in dump file! There are also 4 java_util_List$get but they are java_util_List$get$89 to java_util_List$get$92. I guess the rest are collected somehow. Although the "method classes" won't increase anymore, but those java_util_List$iterator and java_util_Random$nextInt will never be collected!
> I guess the number of those "method classes" is related to the number and frequency of threads excuting the groovy script. Then I try to use groovy class loader to compile a class to excute. I write a TestGroovy.groovy:
> {code:title=TestGroovy.groovy|borderStyle=solid}
> import cn.ming.youxi.Parent
> import cn.ming.youxi.TestClazz
> class TestGroovy implements Parent {
> @Override
> void run() {
> Random random = new Random();
> List list = Arrays.asList(
> random.nextInt(),
> random.nextInt(),
> random.nextInt(),
> random.nextInt(),
> )
> list.get(2);
> println(random.nextInt());
> }
> }
> {code}
> And the code to compile and call it:
> {code:title=TodoJava.java|borderStyle=solid}
> public static void main(String[] args) throws Exception {
> GroovyClassLoader loader = new GroovyClassLoader();
> Class clazz = loader.parseClass(new File("TestGroovy.groovy"));
> Parent parent = (Parent) clazz.newInstance();
> InputStreamReader is_reader = new InputStreamReader(System.in);
> String str = new BufferedReader(is_reader).readLine();
> while (true){
> for (int i = 0; i<10; ++i) {
> MyGroovyClassThread myGroovyClassThread = new MyGroovyClassThread(parent);
> myGroovyClassThread.run();
> }
> Thread.sleep(50);
> }
> }
> static class MyGroovyClassThread extends Thread {
> Parent parent;
> boolean interupt = false;
> public MyGroovyClassThread(Parent parent) {
> this.parent = parent;
> }
> public MyGroovyClassThread(Parent parent, boolean interupt) {
> this.parent = parent;
> this.interupt = interupt;
> }
> public void run() {
> parent.run();
> }
> }
> {code}
> There are java_util_Random$nextInt and java_util_List$get all the same. However the number of each "method class" is only one.
> I think this is a critical bug in groovy, cause in fact I didn't compile the code many times but the script still creates large number of "method classes". If I use groovy script in my server, in order to prevent my website oom, I have to adjust the size of permgen!
> Here is my all java test code:
> {code:title=TodoJava.java|borderStyle=solid}
> package cn.ming.youxi;
> import groovy.lang.GroovyClassLoader;
> import groovy.lang.Script;
> import javax.script.*;
> import java.io.*;
> import java.net.URISyntaxException;
> import java.util.HashMap;
> import java.util.Map;
> import java.util.Random;
> public class TodoJava {
> public static void main2(String[] args) throws ScriptException, IOException, InterruptedException {
> ScriptEngineManager m = new ScriptEngineManager();
> ScriptEngine engine = m.getEngineByName("groovy");
> CompiledScript script = ((Compilable) engine).compile(new FileReader("/Users/liming_liu/Documents/workspace/groovy-demo/test-script.groovy"));
> InputStreamReader is_reader = new InputStreamReader(System.in);
> String str = new BufferedReader(is_reader).readLine();
> while (true) {
> for (int i = 0; i < 10; ++i) {
> MyThread myThread = new MyThread(engine, script);
> myThread.start();
> }
> Thread.sleep(50);
> }
> }
> static class MyThread extends Thread {
> ScriptEngine engine;
> CompiledScript script;
> public MyThread(ScriptEngine engine, CompiledScript script) {
> this.engine = engine;
> this.script = script;
> }
> public void run() {
> TestClazz testClazz = new TestClazz(new Random().nextInt());
> Map<String, Object> params = new HashMap<String, Object>();
> params.put("clazz", testClazz);
> Bindings bindings = engine.createBindings();
> bindings.putAll(params);
> try {
> System.out.println(script.eval(bindings));
> } catch (ScriptException e) {
> e.printStackTrace();
> }
> }
> }
> public static void main1(String[] args) throws ScriptException, IOException, IllegalAccessException, InstantiationException, InterruptedException {
> GroovyClassLoader loader = new GroovyClassLoader();
> Class clazz = loader.parseClass(new File("/Users/liming_liu/Documents/workspace/groovy-demo/test-script.groovy"));
> Random random = new Random();
> Script groovyClass = (Script) clazz.newInstance();
> InputStreamReader is_reader = new InputStreamReader(System.in);
> String str = new BufferedReader(is_reader).readLine();
> while (true) {
> for (int i = 0; i < 10; ++i) {
> MyScriptClassThread myThread = new MyScriptClassThread(groovyClass);
> myThread.start();
> }
> Thread.sleep(50);
> }
> }
> static class MyScriptClassThread extends Thread {
> Script groovyClass;
> boolean interupt = false;
> public MyScriptClassThread(Script groovyClass) {
> this.groovyClass = groovyClass;
> }
> public MyScriptClassThread(Script groovyClass, boolean interupt) {
> this.groovyClass = groovyClass;
> this.interupt = interupt;
> }
> public void run() {
> TestClazz testClazz = new TestClazz(new Random().nextInt());
> Map<String, Object> params = new HashMap<String, Object>();
> params.put("clazz", testClazz);
> groovyClass.setProperty("clazz", testClazz);
> System.out.println(groovyClass.run());
> }
> }
> public static void main(String[] args) throws URISyntaxException, IOException, IllegalAccessException, InstantiationException, InterruptedException {
> GroovyClassLoader loader = new GroovyClassLoader();
> Class clazz = loader.parseClass(new File("/Users/liming_liu/Documents/workspace/groovy-demo/TestGroovy.groovy"));
> Parent parent = (Parent) clazz.newInstance();
> InputStreamReader is_reader = new InputStreamReader(System.in);
> String str = new BufferedReader(is_reader).readLine();
> while (true){
> for (int i = 0; i<10; ++i) {
> MyGroovyClassThread myGroovyClassThread = new MyGroovyClassThread(parent);
> myGroovyClassThread.run();
> }
> Thread.sleep(50);
> }
> }
> static class MyGroovyClassThread extends Thread {
> Parent parent;
> boolean interupt = false;
> public MyGroovyClassThread(Parent parent) {
> this.parent = parent;
> }
> public MyGroovyClassThread(Parent parent, boolean interupt) {
> this.parent = parent;
> this.interupt = interupt;
> }
> public void run() {
> parent.run();
> }
> }
> }
> {code}
--
This message was sent by Atlassian JIRA
(v6.3.15#6346)