groovy-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Daniel Sun (JIRA)" <j...@apache.org>
Subject [jira] [Commented] (GROOVY-9068) GroovyScriptEngine causes Metaspace OOM
Date Thu, 11 Apr 2019 05:51:00 GMT

    [ https://issues.apache.org/jira/browse/GROOVY-9068?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16815091#comment-16815091
] 

Daniel Sun commented on GROOVY-9068:
------------------------------------

{{GroovyCodeSource}} object is not cacheable by default, please try:


{code:java}
import groovy.transform.CompileStatic
import org.codehaus.groovy.runtime.InvokerHelper
import org.codehaus.groovy.runtime.memoize.ConcurrentCommonCache
import org.codehaus.groovy.runtime.memoize.EvictableCache

@CompileStatic
class GroovyUtils {
    private static EvictableCache<String, Class> SCRIPT_CONTENT_CACHE = new ConcurrentCommonCache<>()

    static eval(String scriptPath, Map bindingMap = [:]) {
        def scriptClass = SCRIPT_CONTENT_CACHE.getAndPut(scriptPath, { key ->
            return new GroovyClassLoader().parseClass(GroovyUtils.getResourceAsStream(key).getText('UTF-8'))
        })

        return InvokerHelper.createScript(scriptClass, new Binding(bindingMap)).run()
    }
}
{code}


> 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
>            Priority: Major
>              Labels: GroovyScriptEngineImpl, Metaspace, OOM
>
> 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)

Mime
View raw message