Jingfei Hu created GROOVY-9068:
----------------------------------

             Summary: GroovyScriptEngine causes Metaspace OOM
                 Key: GROOVY-9068
                 URL: https://issues.apache.org/jira/browse/GROOVY-9068
             Project: Groovy
          Issue Type: Bug
          Components: GroovyScriptEngine
    Affects Versions: 2.4.9
         Environment: macOS Mojave, MacBook Pro (Retina, 15-inch, Mid 2015)
            Reporter: Jingfei Hu


Hello team,

We've encountered this troublesome Metaspace OOM in our application recently as 
the number of groovy scripts increases. The groovy usage pattern in our 
application is evaluating scripts adhoc and directly. There are no any kinds of 
caches. And we use below code to do the evaluation. 

 
{code:java}
engine.eval(scriptText, bindings);
{code}
We thought the cache of GroovyScriptEngineImpl which is below field would take 
effect, but actually not in the multi-threading context
{code:java}
// script-string-to-generated Class map
private ManagedConcurrentValueMap<String, Class> classMap = new 
ManagedConcurrentValueMap<String, Class>(ReferenceBundle.getSoftBundle());
{code}
So without proper cache, our application continuously leverages the groovy 
class loader of GroovyScriptEngineImpl to parse scripts to generate Class 
objects and fill up Metaspace eventually. 

 

And below code snippets can easily reproduce this.  

 
{code:java}
package com.jingfei;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {

    static final GroovyScriptExecutor groovyScriptExecutor = new 
GroovyScriptExecutor();

    public static void main(String[] args) throws InterruptedException {

        ExecutorService executor = Executors.newFixedThreadPool(10000);

        while (true)  {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        groovyScriptExecutor.executeScript("(1..10).sum()");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}

package com.jingfei;

import java.util.HashMap;
import java.util.Map;

import javax.script.CompiledScript;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

import groovy.lang.GroovyClassLoader;
import org.codehaus.groovy.jsr223.GroovyCompiledScript;
import org.codehaus.groovy.jsr223.GroovyScriptEngineImpl;
/**
 * @author jingfei
 * @version $Id: GroovyScriptExecutor.java, v 0.1 2019-04-02 20:07 jingfei Exp 
$$
 */
public class GroovyScriptExecutor {

    /**  Script Engine Manager */
    private static final ScriptEngineManager factory = new 
ScriptEngineManager();
    /**  Script engine */
    private static final ScriptEngine engine  = 
factory.getEngineByName("groovy");

    public void executeScript(final String scriptText) throws ScriptException {
        System.out.println(engine.eval(scriptText));
    }
}{code}
Looking into the Metaspace dump, we find out within the Metaspace there are 
hundreds of class loader objects named 
*groovy/lang/GroovyClassLoader$InnerLoader* and all of Class meta info loaded 
by them with the naming pattern *ScriptXXXX.*

 

Since the script is all the same, i.e. 
{code:java}
(1..10).sum(){code}
, this behavior seems totally odd to me, because I thought due to the existence 
of the cache, there would be only one class loader created necessary to parse 
the script. 

 

Any help is appreciated to work out this problem! Thanks!



--
This message was sent by Atlassian JIRA
(v7.6.3#76005)

Reply via email to