chengdedeng
7/14/2015 - 2:06 PM

DirectMemorySize.java

[sajia@xxx164 gist]$ sudo -u admin java -cp .:$JAVA_HOME/lib/sa-jdi.jar DirectMemorySize -e `pgrep -u admin java` | grep -v 'Finding object'
Attaching to process ID 2499, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 20.0-b12-internal
NIO direct memory: (in bytes)
  reserved size = 907.541405 MB (951626136 bytes)
  max size      = 3925.375000 MB (4116054016 bytes)
NIO direct memory malloc'd size: 910.697655 MB (954935704 bytes)
[sajia@211 temp]$ java -cp .:$JAVA_HOME/lib/sa-jdi.jar DirectMemorySize     
Usage: java directMemorySize [option] <pid>
                (to connect to a live java process)
   or java directMemorySize [option] <executable> <core>
                (to connect to a core file)
   or java directMemorySize [option] [server_id@]<remote server IP or hostname>
                (to connect to a remote debug server)

where option must be one of:
    -e  to print the actual size malloc'd
    -v  to print verbose info of every live DirectByteBuffer allocated from Java
    -h | -help  to print this help message
[sajia@211 temp]$ java -cp .:$JAVA_HOME/lib/sa-jdi.jar DirectMemorySize `pgrep -u sajia java`
Attaching to process ID 7381, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 20.5-b03
NIO direct memory: (in bytes)
  reserved size = 0.000003 MB (3 bytes)
  max size      = 7152.000000 MB (7499415552 bytes)
[sajia@211 temp]$ java -cp .:$JAVA_HOME/lib/sa-jdi.jar DirectMemorySize -e `pgrep -u sajia java`
Attaching to process ID 7381, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 20.5-b03
NIO direct memory: (in bytes)
  reserved size = 0.000003 MB (3 bytes)
  max size      = 7152.000000 MB (7499415552 bytes)
Finding object size using Printezis bits and skipping over...
Finding object size using Printezis bits and skipping over...
NIO direct memory malloc'd size: 0.007815 MB (8195 bytes)
[sajia@211 temp]$ time java -cp .:$JAVA_HOME/lib/sa-jdi.jar DirectMemorySize `pgrep -u sajia java`      
Attaching to process ID 7381, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 20.5-b03
NIO direct memory: (in bytes)
  reserved size = 0.000003 MB (3 bytes)
  max size      = 7152.000000 MB (7499415552 bytes)

real    0m0.401s
user    0m0.503s
sys     0m0.057s
[sajia@211 temp]$ time java -cp .:$JAVA_HOME/lib/sa-jdi.jar DirectMemorySize -e `pgrep -u sajia java`
Attaching to process ID 7381, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 20.5-b03
NIO direct memory: (in bytes)
  reserved size = 0.000003 MB (3 bytes)
  max size      = 7152.000000 MB (7499415552 bytes)
Finding object size using Printezis bits and skipping over...
Finding object size using Printezis bits and skipping over...
NIO direct memory malloc'd size: 0.007815 MB (8195 bytes)

real    0m4.822s
user    0m2.095s
sys     0m3.216s
[sajia@211 temp]$ time java -cp .:$JAVA_HOME/lib/sa-jdi.jar DirectMemorySize -e -v `pgrep -u sajia java`
Attaching to process ID 7381, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 20.5-b03
NIO direct memory: (in bytes)
  reserved size = 0.000003 MB (3 bytes)
  max size      = 7152.000000 MB (7499415552 bytes)
Currently allocated direct buffers:
  0x00002aaab021df10: capacity = 0.000001 MB (1 bytes), mallocSize = 0.003907 MB (4097 bytes)
  0x00002aaab0843310: capacity = 0.000002 MB (2 bytes), mallocSize = 0.003908 MB (4098 bytes)
Finding object size using Printezis bits and skipping over...
Finding object size using Printezis bits and skipping over...
NIO direct memory malloc'd size: 0.007815 MB (8195 bytes)

real    0m5.097s
user    0m2.164s
sys     0m3.426s
[sajia@211 temp]$ 
$ java -version
java version "1.6.0_30"
Java(TM) SE Runtime Environment (build 1.6.0_30-b12)
Java HotSpot(TM) 64-Bit Server VM (build 20.5-b03, mixed mode)
$ javac -classpath $JAVA_HOME/lib/sa-jdi.jar DirectMemorySize.java 
$ jps
18486 GroovyStarter
23135 Jps
$ java -classpath .:$JAVA_HOME/lib/sa-jdi.jar DirectMemorySize `pgrep java`
Attaching to process ID 18486, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 20.5-b03
NIO direct memory: (in bytes)
  reserved size = 0.000000 MB (0 bytes)
  max size      = 4069.000000 MB (4266655744 bytes)
Also works on core dumps, in the form of:

```
java -cp .:$JAVA_HOME/lib/sa-jdi.jar DirectMemorySize $JAVA_HOME/bin/java core.xxx
```
import java.io.*;
import java.util.*;
import sun.jvm.hotspot.memory.*;
import sun.jvm.hotspot.oops.*;
import sun.jvm.hotspot.debugger.*;
import sun.jvm.hotspot.runtime.*;
import sun.jvm.hotspot.tools.*;
import sun.jvm.hotspot.utilities.*;

public class DirectMemorySize extends Tool {
    private boolean exactMallocMode;
    private boolean verbose;

    public DirectMemorySize(boolean exactMallocMode, boolean verbose) {
        this.exactMallocMode = exactMallocMode;
        this.verbose = verbose;
    }

    public void run() {
        // Ready to go with the database...
        try {
            long reservedMemory = getStaticLongFieldValue("java.nio.Bits", "reservedMemory");
            long directMemory = getStaticLongFieldValue("sun.misc.VM", "directMemory");

            System.out.println("NIO direct memory: (in bytes)");
            System.out.printf("  reserved size = %f MB (%d bytes)\n", toM(reservedMemory), reservedMemory);
            System.out.printf("  max size      = %f MB (%d bytes)\n", toM(directMemory), directMemory);

            if (verbose) {
                System.out.println("Currently allocated direct buffers:");
            }

            if (exactMallocMode || verbose) {
               final long pageSize = getStaticIntFieldValue("java.nio.Bits", "pageSize");
               ObjectHeap heap = VM.getVM().getObjectHeap();
               InstanceKlass deallocatorKlass =
                   SystemDictionaryHelper.findInstanceKlass("java.nio.DirectByteBuffer$Deallocator");
               final LongField addressField = (LongField) deallocatorKlass.findField("address", "J");
               final IntField capacityField = (IntField) deallocatorKlass.findField("capacity", "I");
               final int[] countHolder = new int[1];

               heap.iterateObjectsOfKlass(new DefaultHeapVisitor() {
                   public boolean doObj(Oop oop) {
                       long address = addressField.getValue(oop);
                       if (address == 0) return false; // this deallocator has already been run

                       long capacity = capacityField.getValue(oop);
                       long mallocSize = capacity + pageSize;
                       countHolder[0]++;

                       if (verbose) {
                           System.out.printf("  0x%016x: capacity = %f MB (%d bytes),"
                                                     + " mallocSize = %f MB (%d bytes)\n",
                                             address, toM(capacity), capacity,
                                                      toM(mallocSize), mallocSize);
                       }

                       return false;
                   }
               }, deallocatorKlass, false);

               if (exactMallocMode) {
                   long totalMallocSize = reservedMemory + pageSize * countHolder[0];
                   System.out.printf("NIO direct memory malloc'd size: %f MB (%d bytes)\n",
                                     toM(totalMallocSize), totalMallocSize);
               }
            }
        } catch (AddressException e) {
            System.err.println("Error accessing address 0x"
                               + Long.toHexString(e.getAddress()));
            e.printStackTrace();
        }
    }

    public static long getStaticLongFieldValue(String className, String fieldName) {
        InstanceKlass klass = SystemDictionaryHelper.findInstanceKlass(className);
        LongField field = (LongField) klass.findField(fieldName, "J");
        return field.getValue(klass); // on JDK7 use: field.getValue(klass.getJavaMirror());
    }

    public static int getStaticIntFieldValue(String className, String fieldName) {
        InstanceKlass klass = SystemDictionaryHelper.findInstanceKlass(className);
        IntField field = (IntField) klass.findField(fieldName, "I");
        return field.getValue(klass); // on JDK7 use: field.getValue(klass.getJavaMirror());
    }
    
    public static double toM(long value) {
        return value / (1024 * 1024.0);
    }

    public String getName() {
        return "directMemorySize";
    }

    protected void printFlagsUsage() {
        System.out.println("    -e\tto print the actual size malloc'd");
        System.out.println("    -v\tto print verbose info of every live DirectByteBuffer allocated from Java");
        super.printFlagsUsage();
    }

    public static void main(String[] args) {
        boolean exactMallocMode = false;
        boolean verbose = false;

        // argument processing logic copied from sun.jvm.hotspot.tools.JStack
        int used = 0;
        for (String arg : args) {
            if ("-e".equals(arg)) {
                exactMallocMode = true;
                used++;
            } else if ("-v".equals(arg)) {
                verbose = true;
                used++;
            }
        }

        if (used != 0) {
            args = Arrays.copyOfRange(args, used, args.length);
        }

        DirectMemorySize tool = new DirectMemorySize(exactMallocMode, verbose);
        tool.start(args);
        tool.stop();
    }
}