Search code examples
javajvm-hotspotpermgen

Why would jmap -permstat report more than MaxPermSize bytes used?


I've taken a the jmap -permstat output of a JVM (reported as version 24.76-b04), it reports the following totals:

total = 5190 76930 1031431696 N/A alive=1, dead=5189 N/A

Which should be the number of entries, classes using this information, bytes used for metadata and liveness information.

Now, I wonder why it would report 1031431696 bytes, just shy of a Gigabyte when I startup my VM with -XX:MaxPermSize=256m. Can someone shed light on how the number is calculated?

Don't know if it's relevant but this is using Rhino with ~3k entries being DefiningClassLoaders.


Solution

  • I have looked a bit at the code for the jmap -permstat implementation. The bytes used value is an estimation based on sizes for the different types of data a classloader loads (methods, fields, interfaces etc).

    The top level method that does the size calculation for a loaded class is sun.jvm.hotspot.tools.PermStat.computeSize:

    private long computeSize(InstanceKlass k) {
      long size = 0L;
    
      // the InstanceKlass object itself
      size += k.getObjectSize();
    
      // Constant pool
      ConstantPool cp = k.getConstants();
      size += cp.getObjectSize();
      size += objectSize(cp.getCache());
      size += objectSize(cp.getTags());
    
      // Interfaces
      size += arraySize(k.getLocalInterfaces());
      size += arraySize(k.getTransitiveInterfaces());
    
      // Inner classes
      size += objectSize(k.getInnerClasses());
    
      // Fields
      size += objectSize(k.getFields());
    
      // Methods
      ObjArray methods = k.getMethods();
      int nmethods = (int) methods.getLength();
      if (nmethods != 0L) {
         size += methods.getObjectSize();
         for (int i = 0; i < nmethods; ++i) {
            Method m = (Method) methods.getObjAt(i);
            size += m.getObjectSize();
            size += objectSize(m.getConstMethod());
         }
      }
    
      // MethodOrdering - an int array that records the original
      // ordering of methods in the class file
      size += arraySize(k.getMethodOrdering());
    
      return size;
    }
    

    My guess is that this estimation fails horribly in your case. The reason it fails is hard to say without checking the object size calculations in detail for your JVM version in combination with the characteristics of the loaded classes in your application.

    Also, the Troubleshooting Guide for Java SE 6 with HotSpot VM mentions that the value is an approximation.

    Bottom line, take the bytes used value with a grain of salt.