[ https://issues.apache.org/jira/browse/GROOVY-8113?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17279013#comment-17279013 ]
João Paulo commented on GROOVY-8113: ------------------------------------ I have found a workaround for this. It's not so simple to do and quite ugly to see what was needed to solve this issue. Basically, I clean myself all metaclasses after run the script. The code below use GroovyShell instead of SimpleTemplateEngine, but wok's for both cases. In all cases that I was using SimpleTemplateEngine, I have created a cache of Template and only reuse it instead of always create a new template. {code:java} Field fieldParallelLockMap = ClassLoader.getDeclaredField('parallelLockMap') fieldParallelLockMap.setAccessible(true) final Script groovyScript = new GroovyShell().parse('scriptAsString') groovyScript.run() final Class theClass = groovyScript.metaClass.theClass final String name = theClass.name ClassLoader classLoader = theClass.classLoader final ClassLoader parent = classLoader.parent if (parent != null) { ((GroovyClassLoader) parent).removeClassCacheEntry(theClass.name) final List<String> classNamesToRemove = ["groovy.runtime.metaclass.${name}MetaClass".toString(), "${name}BeanInfo".toString(), "${name}Customizer".toString()] ClassLoader classLoaderToRemoveClasses = classLoader while (classLoaderToRemoveClasses != null) { final Map<String, Object> parallelLockMap = (Map<String, Object>) fieldParallelLockMap.get(classLoaderToRemoveClasses) if (parallelLockMap != null) { parallelLockMap.entrySet().removeIf { classNamesToRemove.contains(it.key) } } classLoaderToRemoveClasses = classLoaderToRemoveClasses.parent } } InvokerHelper.removeClass(theClass){code} > Groovy script/template engine produce memory leak > -------------------------------------------------- > > Key: GROOVY-8113 > URL: https://issues.apache.org/jira/browse/GROOVY-8113 > Project: Groovy > Issue Type: Bug > Components: GroovyScriptEngine, Templating > Affects Versions: 2.4.8 > Environment: Groovy 2.4.8, Oracle Java JDK 8u121 > Reporter: Artyom Kravchenko > Priority: Critical > Attachments: merged_path.png, object_explorer.png > > > There is a simple way to produce OutOfMemoryException if run infinite > numbers of groovy scripts or evaluate groovy templates. > Simple test to reproduce a memory leak(run groovy script and make template > inside the script): > set max heap size: -Xmx7m > {code:java} > import javax.script.*; > import java.util.HashMap; > import java.util.Map; > public class OutOfMemoryTest { > private static String TEMPLATE = "Dear ${fullName} please go away at > ${time}"; > private static String SCRIPT = "def run(args) {\n" + > "def bindings = ['fullName' : 'Ali Baba', 'time' : new Date()]\n" > + > "println > args.templateEngine.createTemplate(args.template).make(bindings).toString()\n" > + > "}"; > public static void main(String... args) { > // > GroovySystem.getMetaClassRegistry().getMetaClassCreationHandler().setDisableCustomMetaClassLookup(true); > while(true) { > ScriptEngineManager engineManager = new ScriptEngineManager(); > ScriptEngine engine = engineManager.getEngineByName("groovy"); > try { > engine.eval(SCRIPT); > } catch (ScriptException e) { > throw new RuntimeException(e); > } > groovy.text.SimpleTemplateEngine templateEngine = new > groovy.text.SimpleTemplateEngine(); > Invocable inv = (Invocable) engine; > Map<String, Object> params = new HashMap<>(); > params.put("template", TEMPLATE); > params.put("templateEngine", templateEngine); > Object[] runArgs = { params }; > try { > inv.invokeFunction("run", runArgs); > } catch (ScriptException | NoSuchMethodException e) { > e.printStackTrace(); > } > } > } > } > {code} > The main cause of issue is Java class loader. Two instances: > sun.misc.Launcher.AppClassLoader and sun.misc.Launcher.ExtClassLoader > (parent for first one), each of them has a strong reference: > {code:java} > ConcurrentHashMap<String, Object> parallelLockMap > {code} > It collects all loaded classes (and never releases their) for application > including newly created script/template classes. > The main reason of introduction of this reference described here: > https://blogs.oracle.com/dholmes/entry/parallel_classloading_revisited_fully_concurrent > For each iteration of my test I see that parallelLockMap grows with 6 new > entries: > {code:java} > groovy.runtime.metaclass.Script94MetaClass > Script94BeanInfo > Script94Customizer > groovy.runtime.metaclass.SimpleTemplateScript94MetaClass > SimpleTemplateScript94BeanInfo > SimpleTemplateScript94Customizer > {code} > See memory snapshots(attached): objects_explorer, merged_path. > I have found a partial solution of the issue, if I add this code line to my > test: > {code:java} > > GroovySystem.getMetaClassRegistry().getMetaClassCreationHandler().setDisableCustomMetaClassLookup(true); > {code} > I see that classes like: > {code:java} > groovy.runtime.metaclass.Script94MetaClass > groovy.runtime.metaclass.SimpleTemplateScript94MetaClass > {code} > goes away (groovy engine don't ask ClassLoader to find this classes on class > path therefore such classes are not collected by ClassLoader) but classes > like > {code:java} > Script94BeanInfo > Script94Customizer > SimpleTemplateScript94BeanInfo > SimpleTemplateScript94Customizer > {code} > still has affect on memory. > Dear developers, could you please point me any advice/workarounds for this > issue. It will be fine to get rig this issue in future releases. -- This message was sent by Atlassian Jira (v8.3.4#803005)