[jira] [Comment Edited] (GROOVY-9068) GroovyScriptEngine causes Metaspace OOM
[ https://issues.apache.org/jira/browse/GROOVY-9068?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel=16815091#comment-16815091 ] Daniel Sun edited comment on GROOVY-9068 at 4/11/19 12:33 PM: -- {{GroovyCodeSource}} object is not cacheable by default, please try with Groovy 2.5.6+ : {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 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} P.S. You can use MD5 of script text as cache key. was (Author: daniel_sun): {{GroovyCodeSource}} object is not cacheable by default, please try with Groovy 2.5.6+ : {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 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 classMap = new > ManagedConcurrentValueMap(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(1); > 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
[jira] [Comment Edited] (GROOVY-9068) GroovyScriptEngine causes Metaspace OOM
[ https://issues.apache.org/jira/browse/GROOVY-9068?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel=16815091#comment-16815091 ] Daniel Sun edited comment on GROOVY-9068 at 4/11/19 5:52 AM: - {{GroovyCodeSource}} object is not cacheable by default, please try with Groovy 2.5.6+ : {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 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} was (Author: daniel_sun): {{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 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 classMap = new > ManagedConcurrentValueMap(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(1); > 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
[jira] [Comment Edited] (GROOVY-9068) GroovyScriptEngine causes Metaspace OOM
[ https://issues.apache.org/jira/browse/GROOVY-9068?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel=16815089#comment-16815089 ] Daniel Sun edited comment on GROOVY-9068 at 4/11/19 5:51 AM: - I tried to execute {{(1..10).sum()}} for 1 times with the default JVM heap size, everything is OK. {code:java} def sh = new GroovyShell() 1.times { sh.evaluate "(1..10).sum()" } {code} The following code you provided is not thread safe. {code:java} Class getScriptClass(String script) throws CompilationFailedException { Class clazz = classMap.get(script); if (clazz != null) { return clazz; } // The breakpoint is below line clazz = loader.parseClass(script, generateScriptName()); classMap.put(script, clazz); return clazz; } {code} was (Author: daniel_sun): I tried to execute {{(1..10).sum()}} for 1 times with the default JVM heap size, everything is OK. {code:java} def sh = new GroovyShell() 1.times { sh.evaluate "(1..10).sum()" } {code} The following code you provided is not thread safe. {code:java} Class getScriptClass(String script) throws CompilationFailedException { Class clazz = classMap.get(script); if (clazz != null) { return clazz; } // The breakpoint is below line clazz = loader.parseClass(script, generateScriptName()); classMap.put(script, clazz); return clazz; } {code} Please try the following code with Groovy 2.5.6: {code:java} private static final GroovyClassLoader GCL = new GroovyClassLoader(); public static Object evaluate(String scriptText) { Class scriptClass = GCL.parseClass(scriptText); return InvokerHelper.createScript(scriptClass, new Binding()).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 classMap = new > ManagedConcurrentValueMap(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(1); > 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)
[jira] [Comment Edited] (GROOVY-9068) GroovyScriptEngine causes Metaspace OOM
[ https://issues.apache.org/jira/browse/GROOVY-9068?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel=16810820#comment-16810820 ] Daniel Sun edited comment on GROOVY-9068 at 4/5/19 4:43 PM: Please try 2.5.6+, we tweaked the implementation of evaluating scripts. If the content of scripts are same, groovy will not create new classes. P.S. as 2.4.x will not be maintained any more, we strongly recommend Groovy users to migrate to 2.5.x. was (Author: daniel_sun): Please try 2.5.6+, we tweaked the implementation of evaluating scripts. If the content of scripts are same, groovy will not create new classes. > 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 classMap = new > ManagedConcurrentValueMap(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(1); > 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 *Script.* > > 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)