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?
Young Generation. Most newly allocated objects are allocated in the young generation (see Figure 1), which is typically small and collected frequently.
When objects are garbage collected from the Old Generation, it is a major garbage collection event. Permanent Generation: Metadata such as classes and methods are stored in the Permanent Generation. Classes that are no longer in use may be garbage collected from the Permanent Generation.
A java. lang. OutOfMemoryError: PermGen Space is a runtime error in Java which occurs when the permanent generation (PermGen) area in memory is exhausted. The PermGen area of the Java heap is used to store metadata such as class declarations, methods and object arrays.
The main reason for removing PermGen in Java 8 is: It is very hard to predict the required size of PermGen. It is fixed size at startup, so difficult to tune. Future improvements were limited by PermGen space.
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With