Issue Type: Bug Bug
Assignee: Justin Ryan
Components: job-dsl-plugin
Created: 11/Jul/14 4:41 PM
Description:

A user reported an exhaustion of the permanent generation. jmap -histo:live showed that there were several hundred GroovyClassLoader instances in heap, and further analysis tracked down the problem to usage of the Job DSL plugin with Groovy mixins.

If you have a DSL

job {
  name 'generated'
  description("generated on ${new Date()}")
}

then each time you run it, a new GroovyClassLoader is added to the heap. These do not seem to get collected easily, since they are held via a SoftReference in Groovy somewhere, but this /script forces soft references to be cleared:

for (i = 1; ; i *= 2) {
  System.gc();
  new Object[i];
}

After running that, just one GroovyClassLoader remains, held from ASTTransformationVisitor.compUnit—an apparent memory leak in Groovy, but at least limited to holding the loader from one build at a time.

If I change the DSL to be

javaposse.jobdsl.dsl.Job.description2 = {String d ->
  description(d);
}
job {
  name 'generated'
  description2 "generated on ${new Date()}"
}

(recommended monkey-patching) then I see the same behavior. But if I change the DSL to be

class JobMixin {
  public Object description2(String d) {
    return description(d.toUpperCase());
  }
}
javaposse.jobdsl.dsl.Job.mixin(JobMixin);
job {
  name 'generated'
  description2 "generated on ${new Date()}"
}

then I get one GroovyClassLoader per run of the DSL, and they do not get collected even after forcing full GC. Besides the leak of one from ASTTransformationVisitor.compUnit, the rest are leaked in

this     - value: groovy.lang.GroovyClassLoader #3
 <- parent     - class: groovy.util.GroovyScriptEngine$ScriptClassLoader, value: groovy.lang.GroovyClassLoader #3
  <- delegate     - class: groovy.lang.GroovyClassLoader$InnerLoader, value: groovy.util.GroovyScriptEngine$ScriptClassLoader #3
   <- <classLoader>     - class: JobMixin, value: groovy.lang.GroovyClassLoader$InnerLoader #3
    <- cachedClass     - class: org.codehaus.groovy.reflection.CachedClass, value: JobMixin class JobMixin
     <- mixinClass     - class: org.codehaus.groovy.reflection.MixinInMetaClass, value: org.codehaus.groovy.reflection.CachedClass #150
      <- key     - class: java.util.LinkedHashMap$Entry, value: org.codehaus.groovy.reflection.MixinInMetaClass #3
       <- [5]     - class: java.util.HashMap$Entry[], value: java.util.LinkedHashMap$Entry #7716
        <- table     - class: java.util.LinkedHashMap, value: java.util.HashMap$Entry[] #7123
         <- map     - class: java.util.LinkedHashSet, value: java.util.LinkedHashMap #1581
          <- mixinClasses     - class: groovy.lang.ExpandoMetaClass, value: java.util.LinkedHashSet #45
           <- strongMetaClass     - class: org.codehaus.groovy.reflection.ClassInfo, value: groovy.lang.ExpandoMetaClass #1
            <- $staticClassInfo     - class: javaposse.jobdsl.dsl.Job, value: org.codehaus.groovy.reflection.ClassInfo #320
             <- [353]     - class: java.lang.Object[], value: javaposse.jobdsl.dsl.Job class Job
              <- elementData     - class: java.util.Vector, value: java.lang.Object[] #8736
               <- classes     - class: hudson.PluginFirstClassLoader, value: java.util.Vector #192

(The analysis of root GC references was done using the NetBeans Profiler; VisualVM has the same tool.)

Suggested resolutions:

  1. Document this bug and close without fixing.
  2. Clear mixinClasses from every built-in DSL class after every DSL run. (Possibly a race condition, if you are running multiple DSLs at once.)
  3. Prevent DSLs from adding mixins, just throwing some informative error. (Again this would prevent a possible race condition in the current system: there seems to be nothing preventing unrelated DSLs from clashing over definitions.)
  4. Load DSL classes like Job in their own class loader for each DSL run, rather than loading them from the plugin class loader. That would ensure isolation between DSL runs, as well as preventing memory leaks. This seems like the best approach.
Environment: reported and reproduced in 1.532.2; reported in 1.21, reproduced in 1.24
Project: Jenkins
Labels: performance memory-leak groovy
Priority: Major Major
Reporter: Jesse Glick
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators.
For more information on JIRA, see: http://www.atlassian.com/software/jira

--
You received this message because you are subscribed to the Google Groups "Jenkins Issues" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jenkinsci-issues+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to