Search code examples
out-of-memoryjava-7javapermgen

Flags for PermGen not working as expected: -XX:+CMSClassUnloadingEnabled and -XX:+CMSPermGenSweepingEnabled


I have the following code which generates (intentionally) a PermGen java.lang.OutOfMemoryError:


public class Main {

    public static void main(String[] args) throws InstantiationException, IllegalAccessException, InterruptedException {
        String name = "MyClass";
        DynamicClassLoader cl = new DynamicClassLoader();

        int i = 0;
        while (true) {
            //code for generating the binary for a class to be loaded.
            ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
            cw.visit(Opcodes.V1_7, Opcodes.ACC_PUBLIC, name + ++i, null, "java/lang/Object", null);
            MethodVisitor con = cw.visitMethod(Opcodes.ACC_PUBLIC, "", "()V", null, null);
            con.visitCode();
            con.visitVarInsn(Opcodes.ALOAD, 0);
            con.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "", "()V", false);
            con.visitInsn(Opcodes.RETURN);
            con.visitMaxs(1, 1);
            cw.visitEnd();
            //binary code for class successfully generated
                
            Class clazz = cl.defineClass(name + i, cw.toByteArray());
            Object o = clazz.newInstance();
            System.out.println(o.getClass().getName());
        }
    }
    
    private static class DynamicClassLoader extends ClassLoader {
        public Class defineClass(String name, byte[] b) {
            return defineClass(name, b, 0, b.length);
        }
    }
}

I run this code in Java 7. As expected, it gets java.lang.OutOfMemoryError: PermGen space error. When I try to run this program with the mentioned flags as follows:

java -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled -jar target/permgen.jar

, I still get the same error, at exact the same point as when I run it without flags. I expected that if I put those flags, if not a total refresh of PermGen would occur, at least a partial improvement would be seen. But this is not the case.

Question: Am I misunderstanding the meaning of those flags? Can you detail if that's the case, please? Otherwise, any suggestions?

N.B. Output of java -version is:

java version "1.7.0_95"

OpenJDK Runtime Environment (IcedTea 2.6.4) (7u95-2.6.4-3)

OpenJDK 64-Bit Server VM (build 24.95-b01, mixed mode)


Solution

  • All classloaders keep strong references to all classes they load. In your example, you keep reusing the single instance of DynamicClassLoader. This classloader in turn keeps strong references to all classes you load. So the Garbage Collector never sees unreferenced objects it can collect.

    If you modify your test case to use separate classloaders, the Garbage Collector should be able to identify that your classes are not being used and should reclaim the memory.