mchr
mchr

Reputation: 6251

When does the perm gen get collected?

I work on a Tomcat application which uses the CMS collector along with a memory bar to trigger GC. When I reload webapps I sometimes end up in a situation where the Old gen is full enough to trigger GC but the dead Classloaders don't get collected.

I read that Classes are allocated into the perm gen and guessed that they were therefore being ignored by the Old gen collections. I wrote the following test class to test this theory.

package test;

import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.io.IOUtils;

/*
 JVM Options:
 -server -XX:+UseMembar -XX:+UseConcMarkSweepGC
 -XX:+UseParNewGC -XX:CMSInitiatingOccupancyFraction=80
 -XX:+UseCMSInitiatingOccupancyOnly -Xms100m -Xmx100m
 -XX:PermSize=100m -XX:NewSize=10m -XX:MaxNewSize=10m
 -verbose:gc -Xloggc:gc.log  -XX:+PrintGCTimeStamps
 -XX:+PrintGCDetails
 */
public class ClassLoaderTest extends ClassLoader
{
  @Override
  protected synchronized Class<?> loadClass(String xiName, boolean xiResolve)
      throws ClassNotFoundException
  {
    if (xiName.equals("test"))
    {
      // When asked to load "test", load Example.class
      Class<?> c = Example.class;
      String className = c.getName();
      String classAsPath = className.replace('.', '/') + ".class";
      InputStream stream = c.getClassLoader().getResourceAsStream(classAsPath);
      byte[] classData = null;
      try
      {
        classData = IOUtils.toByteArray(stream);
      }
      catch (IOException e)
      {
        e.printStackTrace();
      }
      return defineClass(className, classData, 0, classData.length);
    }
    return super.loadClass(xiName, xiResolve);
  }

  public static class Example {}
  public static ClassLoaderTest classLoaderTest;

  public static void main(String[] args) throws Exception
  {
    // Allocate CL in new gen
    classLoaderTest = new ClassLoaderTest();

    // Load a class - allocated in perm gen
    classLoaderTest.loadClass("test");

    // Discard CL
    classLoaderTest = null;

    // Pause at end
    Thread.sleep(99 * 60 * 1000);
  }

  public final byte[] mMem = new byte[85 * 1024 * 1024];
}

I ran this class and monitored the output using VisualVM and saw that there were indeed multiple Old and Young gen collections happening without the dead Classloader being collected and therefore the large byte array remained in memory.

VisualVM Visual GC

What would trigger the Perm Gen to be collected?

Upvotes: 9

Views: 3721

Answers (1)

mazaneicha
mazaneicha

Reputation: 9425

Check out JVM options "CMSPermGenSweepingEnabled" and "CMSClassUnloadingEnabled". (There are plenty of discussions on the net re their use.) In particular, the first one suppose to include the PermGen in a garbage collection run. By default, the PermGen space is never included in garbage collection (and thus grows without bounds).

Upvotes: 4

Related Questions