diff agent/src/share/classes/sun/jvm/hotspot/tools/ClassLoaderStats.java @ 8112:b685ca4f4fb9

8008536: Add HotSpot support for printing class loader statistics for JMap Reviewed-by: sla, brutisso
author ehelin
date Wed, 20 Feb 2013 16:41:23 +0100
parents agent/src/share/classes/sun/jvm/hotspot/tools/PermStat.java@da91efe96a93
children 38ea2efa32a7
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/src/share/classes/sun/jvm/hotspot/tools/ClassLoaderStats.java	Wed Feb 20 16:41:23 2013 +0100
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+package sun.jvm.hotspot.tools;
+
+import java.io.*;
+import java.util.*;
+
+import sun.jvm.hotspot.debugger.*;
+import sun.jvm.hotspot.memory.*;
+import sun.jvm.hotspot.oops.*;
+import sun.jvm.hotspot.runtime.*;
+import sun.jvm.hotspot.tools.*;
+import sun.jvm.hotspot.utilities.*;
+
+/**
+  A command line tool to print class loader statistics.
+*/
+
+public class ClassLoaderStats extends Tool {
+   boolean verbose = true;
+
+   public static void main(String[] args) {
+      ClassLoaderStats cls = new ClassLoaderStats();
+      cls.start(args);
+      cls.stop();
+   }
+
+   private static class ClassData {
+      Klass klass;
+      long  size;
+
+      ClassData(Klass klass, long size) {
+         this.klass = klass; this.size = size;
+      }
+   }
+
+   private static class LoaderData {
+      long     numClasses;
+      long     classSize;
+      List     classDetail = new ArrayList(); // List<ClassData>
+   }
+
+   public void run() {
+      printClassLoaderStatistics();
+   }
+
+   private void printClassLoaderStatistics() {
+      final PrintStream out = System.out;
+      final PrintStream err = System.err;
+      final Map loaderMap = new HashMap();
+      // loader data for bootstrap class loader
+      final LoaderData bootstrapLoaderData = new LoaderData();
+      if (verbose) {
+         err.print("finding class loader instances ..");
+      }
+
+      VM vm = VM.getVM();
+      ObjectHeap heap = vm.getObjectHeap();
+      Klass classLoaderKlass = vm.getSystemDictionary().getClassLoaderKlass();
+      try {
+         heap.iterateObjectsOfKlass(new DefaultHeapVisitor() {
+                         public boolean doObj(Oop oop) {
+                            loaderMap.put(oop, new LoaderData());
+                                                        return false;
+                         }
+                      }, classLoaderKlass);
+      } catch (Exception se) {
+         se.printStackTrace();
+      }
+
+      if (verbose) {
+         err.println("done.");
+         err.print("computing per loader stat ..");
+      }
+
+      SystemDictionary dict = VM.getVM().getSystemDictionary();
+      dict.classesDo(new SystemDictionary.ClassAndLoaderVisitor() {
+                        public void visit(Klass k, Oop loader) {
+                           if (! (k instanceof InstanceKlass)) {
+                              return;
+                           }
+                           LoaderData ld = (loader != null) ? (LoaderData)loaderMap.get(loader)
+                                                            : bootstrapLoaderData;
+                           if (ld != null) {
+                              ld.numClasses++;
+                              long size = computeSize((InstanceKlass)k);
+                              ld.classDetail.add(new ClassData(k, size));
+                              ld.classSize += size;
+                           }
+                        }
+                     });
+
+      if (verbose) {
+         err.println("done.");
+         err.print("please wait.. computing liveness");
+      }
+
+      // compute reverse pointer analysis (takes long time for larger app)
+      ReversePtrsAnalysis analysis = new ReversePtrsAnalysis();
+
+      if (verbose) {
+         analysis.setHeapProgressThunk(new HeapProgressThunk() {
+            public void heapIterationFractionUpdate(double fractionOfHeapVisited) {
+               err.print('.');
+            }
+            // This will be called after the iteration is complete
+            public void heapIterationComplete() {
+               err.println("done.");
+            }
+         });
+      }
+
+      try {
+         analysis.run();
+      } catch (Exception e) {
+         // e.printStackTrace();
+         if (verbose)
+           err.println("liveness analysis may be inaccurate ...");
+      }
+      ReversePtrs liveness = VM.getVM().getRevPtrs();
+
+      out.println("class_loader\tclasses\tbytes\tparent_loader\talive?\ttype");
+      out.println();
+
+      long numClassLoaders = 1L;
+      long totalNumClasses = bootstrapLoaderData.numClasses;
+      long totalClassSize  = bootstrapLoaderData.classSize;
+      long numAliveLoaders = 1L;
+      long numDeadLoaders  = 0L;
+
+      // print bootstrap loader details
+      out.print("<bootstrap>");
+      out.print('\t');
+      out.print(bootstrapLoaderData.numClasses);
+      out.print('\t');
+      out.print(bootstrapLoaderData.classSize);
+      out.print('\t');
+      out.print("  null  ");
+      out.print('\t');
+      // bootstrap loader is always alive
+      out.print("live");
+      out.print('\t');
+      out.println("<internal>");
+
+      for (Iterator keyItr = loaderMap.keySet().iterator(); keyItr.hasNext();) {
+         Oop loader = (Oop) keyItr.next();
+         LoaderData data = (LoaderData) loaderMap.get(loader);
+         numClassLoaders ++;
+         totalNumClasses += data.numClasses;
+         totalClassSize  += data.classSize;
+
+         out.print(loader.getHandle());
+         out.print('\t');
+         out.print(data.numClasses);
+         out.print('\t');
+         out.print(data.classSize);
+         out.print('\t');
+
+         class ParentFinder extends DefaultOopVisitor {
+            public void doOop(OopField field, boolean isVMField) {
+               if (field.getID().getName().equals("parent")) {
+                  parent = field.getValue(getObj());
+               }
+            }
+            private Oop parent = null;
+            public Oop getParent() { return parent; }
+         }
+
+         ParentFinder parentFinder = new ParentFinder();
+         loader.iterate(parentFinder, false);
+         Oop parent = parentFinder.getParent();
+         out.print((parent != null)? parent.getHandle().toString() : "  null  ");
+         out.print('\t');
+         boolean alive = (liveness != null) ? (liveness.get(loader) != null) : true;
+         out.print(alive? "live" : "dead");
+         if (alive) numAliveLoaders++; else numDeadLoaders++;
+         out.print('\t');
+         Klass loaderKlass = loader.getKlass();
+         if (loaderKlass != null) {
+            out.print(loaderKlass.getName().asString());
+            out.print('@');
+            out.print(loader.getKlass().getAddress());
+         } else {
+            out.print("    null!    ");
+         }
+         out.println();
+      }
+
+      out.println();
+      // summary line
+      out.print("total = ");
+      out.print(numClassLoaders);
+      out.print('\t');
+      out.print(totalNumClasses);
+      out.print('\t');
+      out.print(totalClassSize);
+      out.print('\t');
+      out.print("    N/A    ");
+      out.print('\t');
+      out.print("alive=");
+      out.print(numAliveLoaders);
+      out.print(", dead=");
+      out.print(numDeadLoaders);
+      out.print('\t');
+      out.print("    N/A    ");
+      out.println();
+   }
+
+   private static long objectSize(Oop oop) {
+      return oop == null ? 0L : oop.getObjectSize();
+   }
+
+   // Don't count the shared empty arrays
+   private static long arraySize(GenericArray arr) {
+     return arr.getLength() != 0L ? arr.getSize() : 0L;
+   }
+
+   private long computeSize(InstanceKlass k) {
+      long size = 0L;
+      // the InstanceKlass object itself
+      size += k.getSize();
+
+      // Constant pool
+      ConstantPool cp = k.getConstants();
+      size += cp.getSize();
+      if (cp.getCache() != null) {
+        size += cp.getCache().getSize();
+      }
+      size += arraySize(cp.getTags());
+
+      // Interfaces
+      size += arraySize(k.getLocalInterfaces());
+      size += arraySize(k.getTransitiveInterfaces());
+
+      // Inner classes
+      size += arraySize(k.getInnerClasses());
+
+      // Fields
+      size += arraySize(k.getFields());
+
+      // Methods
+      MethodArray methods = k.getMethods();
+      int nmethods = (int) methods.getLength();
+      if (nmethods != 0L) {
+         size += methods.getSize();
+         for (int i = 0; i < nmethods; ++i) {
+            Method m = methods.at(i);
+            size += m.getSize();
+            size += m.getConstMethod().getSize();
+         }
+      }
+
+      return size;
+   }
+}