Uploaded image for project: 'Groovy'
  1. Groovy
  2. GROOVY-2875

MetaClassRegistryImpl constantMetaClasses map is leaking resources

    XMLWordPrintableJSON

    Details

    • Type: Bug
    • Status: Closed
    • Priority: Blocker
    • Resolution: Fixed
    • Affects Version/s: 1.5.6
    • Fix Version/s: 1.5.7, 1.6-beta-2
    • Component/s: groovy-jdk
    • Labels:
      None
    • Environment:
      java 1.5.0_14 and java 1.6.0_04

      Description

      The GroovySystem is a class that resides in the AppClassLoader. It's final and only contains static attributes and methods.
      Inside it, we find a MetaClassRegistry static property, that is initialized with a MetaClassRegistryImpl instance.
      The MetaClassRegistryImpl contains a ConcurrentReaderHashMap wich holds references to all Expando classes, and metaclasses.

      And that's the problem. Since the ExpandoClasses and MetaClasses are usually created via script by a GroovyClassLoader, this map prevents them be garbage collected, as well as its GroovyClassLoader.

      In our application, we use a schema similar to a web server. We create a different class loader for each executed script. This is necessary since users want to modificate scripts and see the results imediatelly. We've even tried to use the GroovyScriptEngine in the past, but it has a very big flaw. its performance is terrible in a network (I even opened another issue for this). Also, we like the benefit of having a different class loader per script, since we may have different execution environments.

      As a result, we are suffering the "the dreaded "java.lang.OutOfMemoryError: PermGen space" exception", as described in this article:
      http://blogs.sun.com/fkieviet/entry/classloader_leaks_the_dreaded_java

      To simulate the problem, run the above java program with -Xmx500m option:

      Test.java
      public class Test {
          public static void main(String[] args) throws Exception {
              for (int i = 0; i < Integer.MAX_VALUE; i++) {
                  GroovyClassLoader gcl = new GroovyClassLoader();
                  Leak leak = (Leak)gcl.parseClass(new File("ResourceLeak.groovy")).newInstance();
                  leak.leakResources();
                  if (i % 1000 == 0) {
                      System.out.println(i);
                      System.gc();
                  }
              }
          }
          
          public static interface Leak {
              void leakResources();
          }
      } 

      It uses this script:

      ResourceLeak.groovy
      class ResourceLeak implements Teste.Leak {
          void leakResources() {
      		ResourceLeak.metaClass.'static'.test = { 
      			//Do nothing 
      		}
      		ResourceLeak.test()		
          }
      }
      

      The problem will occur after 7000 executions. In this sample, we need 500Mb heap, otherwise, the heap space will go out of memory before the permgen space. But in our real situation (with bigger classes, and more classes in the class loader) 200Mb heap is enough, since the permgen space is consumed faster.

        Attachments

          Activity

            People

            • Assignee:
              blackdrag Jochen Theodorou
              Reporter:
              vinigodoy Vinícius Godoy de Mendonça
            • Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: