Reputation: 6251
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.
What would trigger the Perm Gen to be collected?
Upvotes: 9
Views: 3721
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