view agent/src/share/classes/sun/jvm/hotspot/tools/PermStat.java @ 113:ba764ed4b6f2

6420645: Create a vm that uses compressed oops for up to 32gb heapsizes Summary: Compressed oops in instances, arrays, and headers. Code contributors are coleenp, phh, never, swamyv Reviewed-by: jmasa, kamg, acorn, tbell, kvn, rasbold
author coleenp
date Sun, 13 Apr 2008 17:43:42 -0400
parents a61af66fc99e
children 7f601f7c9b48
line wrap: on
line source

/*
 * Copyright 2003-2006 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.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 perm. generation statistics.
*/

public class PermStat extends Tool {
   boolean verbose = true;

   public static void main(String[] args) {
      PermStat ps = new PermStat();
      ps.start(args);
      ps.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() {
      printInternStringStatistics();
      printClassLoaderStatistics();
   }

   private void printInternStringStatistics() {
      class StringStat implements StringTable.StringVisitor {
         private int count;
         private long size;
         private OopField stringValueField;

         StringStat() {
            VM vm = VM.getVM();
            SystemDictionary sysDict = vm.getSystemDictionary();
            InstanceKlass strKlass = sysDict.getStringKlass();
            // String has a field named 'value' of type 'char[]'.
            stringValueField = (OopField) strKlass.findField("value", "[C");
         }

         private long stringSize(Instance instance) {
            // We include String content in size calculation.
            return instance.getObjectSize() +
                   stringValueField.getValue(instance).getObjectSize();
         }

         public void visit(Instance str) {
            count++;
            size += stringSize(str);
         }

         public void print() {
            System.out.println(count +
                  " intern Strings occupying " + size + " bytes.");
         }
      }

      StringStat stat = new StringStat();
      StringTable strTable = VM.getVM().getStringTable();
      strTable.stringsDo(stat);
      stat.print();
   }

   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().getHandle());
         } 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 long computeSize(InstanceKlass k) {
      long size = 0L;
      // InstanceKlass object size
      size += k.getObjectSize();

      // add ConstantPool size
      size += k.getConstants().getObjectSize();

      // add ConstantPoolCache, if any
      ConstantPoolCache cpCache = k.getConstants().getCache();
      if (cpCache != null) {
         size += cpCache.getObjectSize();
      }

      // add interfaces size
      ObjArray interfaces = k.getLocalInterfaces();
      size +=  (interfaces.getLength() != 0L)? interfaces.getObjectSize() : 0L;
      ObjArray transitiveInterfaces = k.getTransitiveInterfaces();
      size += (transitiveInterfaces.getLength() != 0L)? transitiveInterfaces.getObjectSize() : 0L;

      // add inner classes size
      TypeArray innerClasses = k.getInnerClasses();
      size += innerClasses.getObjectSize();

      // add fields size
      size += k.getFields().getObjectSize();

      // add methods size
      ObjArray methods = k.getMethods();
      size += (methods.getLength() != 0L)? methods.getObjectSize() : 0L;
      TypeArray methodOrdering = k.getMethodOrdering();
      size += (methodOrdering.getLength() != 0L)? methodOrdering.getObjectSize() : 0;

      // add each method's size
      int numMethods = (int) methods.getLength();
      for (int i = 0; i < numMethods; i++) {
         Method m = (Method) methods.getObjAt(i);
         size += m.getObjectSize();
      }

      return size;
   }
}