[ https://issues.apache.org/jira/browse/GROOVY-6655?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17279197#comment-17279197 ]
João Paulo commented on GROOVY-6655: ------------------------------------ I have found a workaround for this problem: https://issues.apache.org/jira/browse/GROOVY-8113?focusedCommentId=17279013&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-17279013 > GroovyClassLoader parallelLockMap memory leak on Java 7 > ------------------------------------------------------- > > Key: GROOVY-6655 > URL: https://issues.apache.org/jira/browse/GROOVY-6655 > Project: Groovy > Issue Type: Bug > Components: groovy-runtime > Affects Versions: 2.2.2 > Environment: Java 7 > Reporter: Henri Pihkala > Priority: Critical > Labels: classloader, java, leak, memory, metaprogramming > > Java 7 introduced parallel classloading. There is a map in > {{java.lang.ClassLoader}} called {{parallelLockMap}}. This map holds String > keys for class names and the associated Objects used to synchronize on for > each class. > Now, I found that dynamically parsing (perhaps also loading?) and unloading > classes with {{GroovyClassLoader}} leaves obsolete keys in this map. Any time > {{java.lang.ClassLoader#loadClass(..)}} is called, an entry is created for > that class name if it does not exist. > In the default Java delegating classloader paradigm the delegate classloader > is asked for a class first, so a call to the root loader's > {{java.lang.ClassLoader#loadClass(..)}} is made for a whole lot of classes, > including Groovy metaclasses, polluting the map with keys. > !https://dl.dropboxusercontent.com/u/4655232/parallelLockMapMemoryLeak.png! > The entries that remain in the map even after the class and associated > metaclasses themselves have been unloaded have the following two types of > keys: > {{"groovy.runtime.metaclass.MyDynamicallyLoadedClassNameMetaClass"}} > {{"MyDynamicallyLoadedClassNameBeanInfo"}} > A key for the class name itself, ie. {{MyDynamicallyLoadedClassName}}, does > not appear in the map. I am assuming it is never inserted there, but instead > defined by {{GroovyClassLoader}} without asking the parent first. But these > metaclass keys *do* leak into the map, eventually exhausting heap memory even > if the classes themselves get nicely unloaded (from PermGen) when the > {{GroovyClassLoader}} is GC'ed. > Here's a very simple test script that will eventually run out of memory (it > does take some time): > {code:title=MemoryLeakTest.groovy} > String newClass = "class CLASSNAME {}" > while (true) { > GroovyClassLoader gcl = new GroovyClassLoader() > Class clazz = gcl.parseClass(newClass.replace("CLASSNAME", > "NewClass"+System.nanoTime())) > clazz.newInstance() > } > {code} > Be sure to run JVM with > {noformat} > -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled > {noformat} > to make sure you run out of *heap* memory and not *PermGen* space. > One fix for this would be to add a check to {{GroovyClassLoader}} that would > prevent it from delegating to the parent first if the class to be loaded is a > metaclass of a class loaded by that classloader. I am definitely no expert in > this so you might come up with a much better idea. > This has been tested on Groovy 2.0.5 and 2.2.2 and I expect that it affects a > lot of versions running on Java 7. > Sure, it doesn't leak memory at a very fast pace, only a > {{ConcurrentHashMap}} entry for each class loaded and unloaded. I can see how > in many applications this is not a major problem, but please consider for > example a server that automatically checks students' programming assignments > or such. Please do change the issue priority if appropriate. > !https://dl.dropboxusercontent.com/u/4655232/parallelLockMapMemoryLeak2.png! -- This message was sent by Atlassian Jira (v8.3.4#803005)