Search code examples
grailsgroovymemory-leaksclassloaderredeploy

Simple Grails 2.5.1 application leaks classloaders with Groovy 2.4.4


I have problems hot-redeploying a simple Grails application in Tomcat 8.

My setup is the following:

  • Grails 2.5.1 Brand new application, just created with create-app
  • Tomcat 8.0.28 (64 bit Linux binary version)
  • Java 1.8.0_65-b17 HotSpot server VM

Tomcat is also a brand new installation, only modified two things (as I want to use them in production):

server.xml
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true" undeployOldVersions="true">

context.xml
<Context antiResourceLocking="true">

I've restarted the Tomcat server. According to JVisualVM it had 2398 classes loaded. After copying the war file produced by grails prod war, and waiting for the deploy to finish, it had 10 022 classes loaded. After copying the war again, thus triggering a redeploy, it had 16 300 classes.

I made heap dumps after the fist deploy, and the second, and analyzing the classloaders with eclipse MAT, I can see that there is one extra org.apache.catalina.loader.WebappClassLoader with 6 138 loaded classes (so there are two of them in total).

The heap space remained pretty constant, only the MetaSpace usage increased significantly (at about the same rate as the number of classes).

Update

Using MAT to dig deeper, I've noticed that there are always 9 instances keeping the classloader alive. They are instances of org.codehaus.groovy.reflection.ClassInfo (one for each primitive java type wrapper and Void). These ClassInfos are only referenced by java.lang.ClassValue$Entry, which extends WeakReference, so I'm really puzzled how these instances are not getting garbage collected.

Has anyone had similar issues? What could cause this loader to hang around?


Solution

  • This issue is related to https://issues.apache.org/jira/browse/GROOVY-7591

    I do not completely understand the issue, but I will try to describe it shortly:
    The use of ClassValue (due to a JDK bug) prevents objects from being garbage collected. A commit in Groovy 2.4.5 temporarily disabled the use of ClassValue while the JDK bug gets fixed.

    Grails 2.5.1 uses groovy 2.4.4 by default, so to solve the issue I replaced it in BuildConfig.groovy, and rebuilt the application.

    build 'org.codehaus.groovy:groovy-all:2.4.5'
    compile 'org.codehaus.groovy:groovy-all:2.4.5'