Details
-
Bug
-
Status: Open
-
Critical
-
Resolution: Unresolved
-
2.4.8
-
None
-
None
-
Groovy 2.4.8, Oracle Java JDK 8u121
Description
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
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(); } } } }
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:
ConcurrentHashMap<String, Object> parallelLockMap
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:
groovy.runtime.metaclass.Script94MetaClass Script94BeanInfo Script94Customizer groovy.runtime.metaclass.SimpleTemplateScript94MetaClass SimpleTemplateScript94BeanInfo SimpleTemplateScript94Customizer
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:
GroovySystem.getMetaClassRegistry().getMetaClassCreationHandler().setDisableCustomMetaClassLookup(true);
I see that classes like:
groovy.runtime.metaclass.Script94MetaClass groovy.runtime.metaclass.SimpleTemplateScript94MetaClass
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
Script94BeanInfo Script94Customizer SimpleTemplateScript94BeanInfo SimpleTemplateScript94Customizer
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.
Attachments
Attachments
Issue Links
- is related to
-
GROOVY-6655 GroovyClassLoader parallelLockMap memory leak on Java 7
- Closed