-
Bug
-
Resolution: Fixed
-
Major
-
jenkins:2.504.3
workflow-cps:4106.4108.v841a_e1819d4d
When using GStringTemplateEngine in a Pipeline Shared Library, each usage leaks a groovy.tmp.templates.GStringTemplateScript* class in every PluginClassLoader.
This heap memory leak may be significant depending on the scale at which GStringTemplateEngine is being used.
Stacktrace during the loading looks like the following:
"Running CpsFlowExecution[testPipeline2#11]" id=363 state=RUNNABLE at java.base@17.0.8/java.lang.Throwable.fillInStackTrace(Native Method) at java.base@17.0.8/java.lang.Throwable.fillInStackTrace(Throwable.java:798) at java.base@17.0.8/java.lang.Throwable.<init>(Throwable.java:293) at java.base@17.0.8/java.lang.Exception.<init>(Exception.java:85) at java.base@17.0.8/java.lang.ReflectiveOperationException.<init>(ReflectiveOperationException.java:76) at java.base@17.0.8/java.lang.ClassNotFoundException.<init>(ClassNotFoundException.java:71) at java.base@17.0.8/java.net.URLClassLoader.findClass(URLClassLoader.java:445) at jenkins.util.URLClassLoader2.findClass(URLClassLoader2.java:64) at jenkins.ClassLoaderReflectionToolkit.loadClass(ClassLoaderReflectionToolkit.java:148) - locked java.lang.Object@5ad2e734 at hudson.PluginManager$UberClassLoader.computeValue(PluginManager.java:2422) at hudson.PluginManager$UberClassLoader$$Lambda$420/0x0000000131471da0.apply(Unknown Source) at java.base@17.0.8/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708) - locked java.util.concurrent.ConcurrentHashMap$ReservationNode@448fe56f at hudson.PluginManager$UberClassLoader.findClass(PluginManager.java:2415) at java.base@17.0.8/java.lang.ClassLoader.loadClass(ClassLoader.java:592) - locked java.lang.Object@3e8eed00 at java.base@17.0.8/java.lang.ClassLoader.loadClass(ClassLoader.java:525) at PluginClassLoader for script-security//org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxResolvingClassLoader.lambda$loadClass$0(SandboxResolvingClassLoader.java:108) at PluginClassLoader for script-security//org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxResolvingClassLoader$$Lambda$1031/0x0000000131d5dc50.get(Unknown Source) at PluginClassLoader for script-security//org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxResolvingClassLoader.lambda$load$2(SandboxResolvingClassLoader.java:144) at PluginClassLoader for script-security//org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxResolvingClassLoader$$Lambda$1035/0x0000000131d5de78.apply(Unknown Source) at PluginClassLoader for caffeine-api//com.github.benmanes.caffeine.cache.LocalCache.lambda$statsAware$2(LocalCache.java:167) at PluginClassLoader for caffeine-api//com.github.benmanes.caffeine.cache.LocalCache$$Lambda$1032/0x0000000131d2dcf8.apply(Unknown Source) at PluginClassLoader for caffeine-api//com.github.benmanes.caffeine.cache.BoundedLocalCache.lambda$doComputeIfAbsent$14(BoundedLocalCache.java:2704) at PluginClassLoader for caffeine-api//com.github.benmanes.caffeine.cache.BoundedLocalCache$$Lambda$985/0x0000000131d2ca48.apply(Unknown Source) at java.base@17.0.8/java.util.concurrent.ConcurrentHashMap.compute(ConcurrentHashMap.java:1916) - locked java.util.concurrent.ConcurrentHashMap$ReservationNode@2b9cacb6 at PluginClassLoader for caffeine-api//com.github.benmanes.caffeine.cache.BoundedLocalCache.doComputeIfAbsent(BoundedLocalCache.java:2702) at PluginClassLoader for caffeine-api//com.github.benmanes.caffeine.cache.BoundedLocalCache.computeIfAbsent(BoundedLocalCache.java:2684) at PluginClassLoader for caffeine-api//com.github.benmanes.caffeine.cache.LocalCache.computeIfAbsent(LocalCache.java:112) at PluginClassLoader for caffeine-api//com.github.benmanes.caffeine.cache.LocalManualCache.get(LocalManualCache.java:63) at PluginClassLoader for script-security//org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxResolvingClassLoader.load(SandboxResolvingClassLoader.java:138) at PluginClassLoader for script-security//org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxResolvingClassLoader.loadClass(SandboxResolvingClassLoader.java:106) - locked org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxResolvingClassLoader@20863865 at java.base@17.0.8/java.lang.ClassLoader.loadClass(ClassLoader.java:579) - locked org.jenkinsci.plugins.workflow.cps.GroovySourceFileAllowlist$ClassLoaderImpl@5e88245d at java.base@17.0.8/java.lang.ClassLoader.loadClass(ClassLoader.java:579) - locked org.jenkinsci.plugins.workflow.cps.CpsGroovyShell$TimingLoader@42ff36a9 at PluginClassLoader for workflow-cps//org.jenkinsci.plugins.workflow.cps.CpsGroovyShell$TimingLoader.loadClass(CpsGroovyShell.java:216) at java.base@17.0.8/java.lang.ClassLoader.loadClass(ClassLoader.java:579) - locked org.jenkinsci.plugins.workflow.cps.CpsGroovyShell$CleanGroovyClassLoader@59ce95cc at groovy.lang.GroovyClassLoader.loadClass(GroovyClassLoader.java:702) at groovy.lang.GroovyClassLoader.loadClass(GroovyClassLoader.java:812) at java.base@17.0.8/java.lang.ClassLoader.loadClass(ClassLoader.java:579) - locked org.jenkinsci.plugins.workflow.cps.CpsGroovyShell$TimingLoader@467be864 at PluginClassLoader for workflow-cps//org.jenkinsci.plugins.workflow.cps.CpsGroovyShell$TimingLoader.loadClass(CpsGroovyShell.java:216) at java.base@17.0.8/java.lang.ClassLoader.loadClass(ClassLoader.java:579) - locked org.jenkinsci.plugins.workflow.cps.CpsGroovyShell$CleanGroovyClassLoader@63c3957e at groovy.lang.GroovyClassLoader.loadClass(GroovyClassLoader.java:702) at groovy.lang.GroovyClassLoader.loadClass(GroovyClassLoader.java:812) at java.base@17.0.8/java.lang.ClassLoader.loadClass(ClassLoader.java:579) - locked groovy.lang.GroovyClassLoader@1c97f649 at groovy.lang.GroovyClassLoader.loadClass(GroovyClassLoader.java:702) at groovy.lang.GroovyClassLoader.loadClass(GroovyClassLoader.java:812) at java.base@17.0.8/java.lang.ClassLoader.loadClass(ClassLoader.java:579) - locked groovy.lang.GroovyClassLoader@59951061 at groovy.lang.GroovyClassLoader.loadClass(GroovyClassLoader.java:702) at groovy.lang.GroovyClassLoader$InnerLoader.loadClass(GroovyClassLoader.java:450) at groovy.lang.GroovyClassLoader.loadClass(GroovyClassLoader.java:812) at groovy.lang.GroovyClassLoader.loadClass(GroovyClassLoader.java:800) at java.base@17.0.8/java.lang.Class.forName0(Native Method) at java.base@17.0.8/java.lang.Class.forName(Class.java:467) at java.desktop@17.0.8/com.sun.beans.finder.ClassFinder.findClass(ClassFinder.java:103) at java.desktop@17.0.8/java.beans.Introspector.findCustomizerClass(Introspector.java:1125) at java.desktop@17.0.8/java.beans.Introspector.getTargetBeanDescriptor(Introspector.java:1119) at java.desktop@17.0.8/java.beans.Introspector.getBeanInfo(Introspector.java:445) at java.desktop@17.0.8/java.beans.Introspector.getBeanInfo(Introspector.java:195) at groovy.lang.MetaClassImpl$15.run(MetaClassImpl.java:3328) at java.base@17.0.8/java.security.AccessController.executePrivileged(AccessController.java:807) at java.base@17.0.8/java.security.AccessController.doPrivileged(AccessController.java:569) at groovy.lang.MetaClassImpl.addProperties(MetaClassImpl.java:3326) at groovy.lang.MetaClassImpl.initialize(MetaClassImpl.java:3303) - locked groovy.lang.MetaClassImpl@663a15b6 at org.codehaus.groovy.reflection.ClassInfo.getMetaClassUnderLock(ClassInfo.java:289) at org.codehaus.groovy.reflection.ClassInfo.getMetaClass(ClassInfo.java:331) at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.getMetaClass(MetaClassRegistryImpl.java:277) at org.codehaus.groovy.runtime.InvokerHelper.getMetaClass(InvokerHelper.java:905) at groovy.lang.GroovyObjectSupport.getDefaultMetaClass(GroovyObjectSupport.java:61) at groovy.lang.GroovyObjectSupport.<init>(GroovyObjectSupport.java:34) at groovy.lang.Script.<init>(Script.java:42) at groovy.lang.Script.<init>(Script.java:39) at groovy.tmp.templates.GStringTemplateScript2403.<init>(GStringTemplateScript2403.groovy) at java.base@17.0.8/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at java.base@17.0.8/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77) at java.base@17.0.8/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.base@17.0.8/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) at java.base@17.0.8/java.lang.reflect.ReflectAccess.newInstance(ReflectAccess.java:128) at java.base@17.0.8/jdk.internal.reflect.ReflectionFactory.newInstance(ReflectionFactory.java:347) at java.base@17.0.8/java.lang.Class.newInstance(Class.java:645) at groovy.text.GStringTemplateEngine$GStringTemplate.<init>(GStringTemplateEngine.java:207) at groovy.text.GStringTemplateEngine.createTemplate(GStringTemplateEngine.java:115) at groovy.text.TemplateEngine.createTemplate(TemplateEngine.java:41) at jdk.internal.reflect.GeneratedMethodAccessor172.invoke(Unknown Source) at java.base@17.0.8/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base@17.0.8/java.lang.reflect.Method.invoke(Method.java:568) at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:98) at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325) at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1225) at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1034) at org.codehaus.groovy.runtime.callsite.PojoMetaClassSite.call(PojoMetaClassSite.java:46) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116) at PluginClassLoader for workflow-cps//com.cloudbees.groovy.cps.sandbox.DefaultInvoker.methodCall(DefaultInvoker.java:20) at PluginClassLoader for workflow-cps//org.jenkinsci.plugins.workflow.cps.LoggingInvoker.methodCall(LoggingInvoker.java:118) at PluginClassLoader for workflow-cps//com.cloudbees.groovy.cps.impl.ContinuationGroup.methodCall(ContinuationGroup.java:90) at PluginClassLoader for workflow-cps//com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.dispatchOrArg(FunctionCallBlock.java:114) at PluginClassLoader for workflow-cps//com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.fixArg(FunctionCallBlock.java:83) at jdk.internal.reflect.GeneratedMethodAccessor163.invoke(Unknown Source) at java.base@17.0.8/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base@17.0.8/java.lang.reflect.Method.invoke(Method.java:568) at PluginClassLoader for workflow-cps//com.cloudbees.groovy.cps.impl.ContinuationPtr$ContinuationImpl.receive(ContinuationPtr.java:72) at PluginClassLoader for workflow-cps//com.cloudbees.groovy.cps.impl.LocalVariableBlock$LocalVariable.get(LocalVariableBlock.java:39) at PluginClassLoader for workflow-cps//com.cloudbees.groovy.cps.LValueBlock$GetAdapter.receive(LValueBlock.java:30) at PluginClassLoader for workflow-cps//com.cloudbees.groovy.cps.impl.LocalVariableBlock.evalLValue(LocalVariableBlock.java:28) at PluginClassLoader for workflow-cps//com.cloudbees.groovy.cps.LValueBlock$BlockImpl.eval(LValueBlock.java:55) at PluginClassLoader for workflow-cps//com.cloudbees.groovy.cps.LValueBlock.eval(LValueBlock.java:16) at PluginClassLoader for workflow-cps//com.cloudbees.groovy.cps.Next.step(Next.java:83) at PluginClassLoader for workflow-cps//com.cloudbees.groovy.cps.Continuable.run0(Continuable.java:147) at PluginClassLoader for workflow-cps//org.jenkinsci.plugins.workflow.cps.SandboxContinuable.access$001(SandboxContinuable.java:17) at PluginClassLoader for workflow-cps//org.jenkinsci.plugins.workflow.cps.SandboxContinuable.run0(SandboxContinuable.java:49) at PluginClassLoader for workflow-cps//org.jenkinsci.plugins.workflow.cps.CpsThread.runNextChunk(CpsThread.java:180) at PluginClassLoader for workflow-cps//org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.run(CpsThreadGroup.java:419) at PluginClassLoader for workflow-cps//org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:327) at PluginClassLoader for workflow-cps//org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:292) at PluginClassLoader for workflow-cps//org.jenkinsci.plugins.workflow.cps.CpsVmExecutorService.lambda$wrap$4(CpsVmExecutorService.java:140) at PluginClassLoader for workflow-cps//org.jenkinsci.plugins.workflow.cps.CpsVmExecutorService$$Lambda$1105/0x0000000131de64c8.call(Unknown Source) at java.base@17.0.8/java.util.concurrent.FutureTask.run(FutureTask.java:264) at hudson.remoting.SingleLaneExecutorService$1.run(SingleLaneExecutorService.java:139) at jenkins.util.ContextResettingExecutorService$1.run(ContextResettingExecutorService.java:28) at jenkins.security.ImpersonatingExecutorService$1.run(ImpersonatingExecutorService.java:68) at jenkins.util.ErrorLoggingExecutorService.lambda$wrap$0(ErrorLoggingExecutorService.java:51) at jenkins.util.ErrorLoggingExecutorService$$Lambda$1102/0x0000000131dc3708.run(Unknown Source) at java.base@17.0.8/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) at java.base@17.0.8/java.util.concurrent.FutureTask.run(FutureTask.java:264) at java.base@17.0.8/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) at java.base@17.0.8/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) at PluginClassLoader for workflow-cps//org.jenkinsci.plugins.workflow.cps.CpsVmExecutorService$1.call(CpsVmExecutorService.java:53) at PluginClassLoader for workflow-cps//org.jenkinsci.plugins.workflow.cps.CpsVmExecutorService$1.call(CpsVmExecutorService.java:50) at org.codehaus.groovy.runtime.GroovyCategorySupport$ThreadCategoryInfo.use(GroovyCategorySupport.java:136) at org.codehaus.groovy.runtime.GroovyCategorySupport.use(GroovyCategorySupport.java:275) at PluginClassLoader for workflow-cps//org.jenkinsci.plugins.workflow.cps.CpsVmExecutorService.lambda$categoryThreadFactory$0(CpsVmExecutorService.java:50) at PluginClassLoader for workflow-cps//org.jenkinsci.plugins.workflow.cps.CpsVmExecutorService$$Lambda$1103/0x0000000131de4228.run(Unknown Source) at java.base@17.0.8/java.lang.Thread.run(Thread.java:833) Locked synchronizers: count = 1 - java.util.concurrent.ThreadPoolExecutor$Worker@72de4195
How To Reproduce
- Create a Shared Library with a variable like the following:
# vars/gstringTemplate.groovy def call(templateStr, binding) { def engine = new groovy.text.GStringTemplateEngine() def template = engine.createTemplate(templateStr).make(binding) return template.toString() }
- Create a Pipeline with the following script:
@Library("testLib@main") _ def defaultTemplateStr = 'Hello ${name}!' node { for(int i=0; i<10000; i++) { println gstringTemplate(defaultTemplateStr, ["name": "Leak" + i]) } }
- Build the pipeline
- Generate a Heapdump and observe the number of objects in the parallelLockMap of the URLClassLoader2 objects (each being the PluginClassLoader of a particular plugin. They contain the 10000 references to the groovy.tmp.templates.GStringTemplateScript* objects generate by the engine.
References
- https://github.com/apache/groovy/blob/41b990d0a20e442f29247f0e04cbed900f3dcad4/subprojects/groovy-templates/src/main/groovy/groovy/text/GStringTemplateEngine.java#L192-L204
- https://github.com/apache/groovy/blob/41b990d0a20e442f29247f0e04cbed900f3dcad4/subprojects/groovy-templates/src/main/groovy/groovy/text/GStringTemplateEngine.java#L103-L105
- https://github.com/jenkinsci/workflow-cps-plugin/blob/4150.ve20ca_b_a_a_2815/doc/classloader.md
- https://github.com/jenkinsci/jenkins/blob/jenkins-2.504.3/core/src/main/java/hudson/PluginManager.java#L2419-L2431
- relates to
-
JENKINS-75675 Refactor class loading logic in order to reduce memory consumption
-
- Closed
-
- links to