1 /*
   2  * Copyright 2003-2006 Sun Microsystems, Inc.  All Rights Reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  20  * CA 95054 USA or visit www.sun.com if you need additional information or
  21  * have any questions.
  22  *  
  23  */
  24 
  25 package sun.jvm.hotspot.tools;
  26 
  27 import java.io.*;
  28 import java.util.*;
  29 
  30 import sun.jvm.hotspot.debugger.*;
  31 import sun.jvm.hotspot.memory.*;
  32 import sun.jvm.hotspot.oops.*;
  33 import sun.jvm.hotspot.runtime.*;
  34 import sun.jvm.hotspot.tools.*;
  35 import sun.jvm.hotspot.utilities.*;
  36 
  37 /**
  38   A command line tool to print perm. generation statistics.
  39 */
  40 
  41 public class PermStat extends Tool {
  42    boolean verbose = true;
  43 
  44    public static void main(String[] args) {
  45       PermStat ps = new PermStat();
  46       ps.start(args);
  47       ps.stop();
  48    }
  49 
  50    private static class ClassData {
  51       Klass klass;
  52       long  size;
  53 
  54       ClassData(Klass klass, long size) {
  55          this.klass = klass; this.size = size;
  56       }
  57    }
  58 
  59    private static class LoaderData {
  60       long     numClasses;
  61       long     classSize;
  62       List     classDetail = new ArrayList(); // List<ClassData>
  63    }
  64 
  65    public void run() {
  66       printInternStringStatistics();
  67       printClassLoaderStatistics();
  68    }
  69 
  70    private void printInternStringStatistics() {
  71       class StringStat implements StringTable.StringVisitor {
  72          private int count;
  73          private long size;
  74          private OopField stringValueField;
  75 
  76          StringStat() {
  77             VM vm = VM.getVM();
  78             SystemDictionary sysDict = vm.getSystemDictionary();
  79             InstanceKlass strKlass = sysDict.getStringKlass();
  80             // String has a field named 'value' of type 'char[]'.
  81             stringValueField = (OopField) strKlass.findField("value", "[C");
  82          }
  83  
  84          private long stringSize(Instance instance) {
  85             // We include String content in size calculation.
  86             return instance.getObjectSize() +
  87                    stringValueField.getValue(instance).getObjectSize();
  88          }
  89 
  90          public void visit(Instance str) {
  91             count++;
  92             size += stringSize(str);
  93          }
  94 
  95          public void print() {
  96             System.out.println(count + 
  97                   " intern Strings occupying " + size + " bytes.");
  98          }
  99       }
 100 
 101       StringStat stat = new StringStat();
 102       StringTable strTable = VM.getVM().getStringTable();
 103       strTable.stringsDo(stat);
 104       stat.print(); 
 105    }
 106 
 107    private void printClassLoaderStatistics() {
 108       final PrintStream out = System.out;
 109       final PrintStream err = System.err;
 110       final Map loaderMap = new HashMap();
 111       // loader data for bootstrap class loader
 112       final LoaderData bootstrapLoaderData = new LoaderData();
 113       if (verbose) {
 114          err.print("finding class loader instances ..");
 115       }
 116 
 117       VM vm = VM.getVM();
 118       ObjectHeap heap = vm.getObjectHeap();
 119       Klass classLoaderKlass = vm.getSystemDictionary().getClassLoaderKlass();
 120       try {
 121          heap.iterateObjectsOfKlass(new DefaultHeapVisitor() {
 122                          public boolean doObj(Oop oop) {
 123                             loaderMap.put(oop, new LoaderData());
 124                                                         return false;
 125                          }
 126                       }, classLoaderKlass);
 127       } catch (Exception se) {
 128          se.printStackTrace();
 129       }
 130 
 131       if (verbose) {
 132          err.println("done.");
 133          err.print("computing per loader stat ..");
 134       }
 135 
 136       SystemDictionary dict = VM.getVM().getSystemDictionary();
 137       dict.classesDo(new SystemDictionary.ClassAndLoaderVisitor() {
 138                         public void visit(Klass k, Oop loader) {
 139                            if (! (k instanceof InstanceKlass)) {
 140                               return;
 141                            }
 142                            LoaderData ld = (loader != null) ? (LoaderData)loaderMap.get(loader) 
 143                                                             : bootstrapLoaderData;
 144                            if (ld != null) {
 145                               ld.numClasses++;
 146                               long size = computeSize((InstanceKlass)k);
 147                               ld.classDetail.add(new ClassData(k, size));
 148                               ld.classSize += size;                               
 149                            }
 150                         }
 151                      });
 152 
 153       if (verbose) {
 154          err.println("done.");
 155          err.print("please wait.. computing liveness");
 156       }
 157 
 158       // compute reverse pointer analysis (takes long time for larger app)
 159       ReversePtrsAnalysis analysis = new ReversePtrsAnalysis();
 160 
 161       if (verbose) {
 162          analysis.setHeapProgressThunk(new HeapProgressThunk() {
 163             public void heapIterationFractionUpdate(double fractionOfHeapVisited) {
 164                err.print('.');
 165             }
 166             // This will be called after the iteration is complete
 167             public void heapIterationComplete() {
 168                err.println("done.");
 169             }
 170          });
 171       }
 172 
 173       try {
 174          analysis.run();
 175       } catch (Exception e) {
 176          // e.printStackTrace();
 177          if (verbose) 
 178            err.println("liveness analysis may be inaccurate ...");
 179       }
 180       ReversePtrs liveness = VM.getVM().getRevPtrs();
 181 
 182       out.println("class_loader\tclasses\tbytes\tparent_loader\talive?\ttype");
 183       out.println();
 184 
 185       long numClassLoaders = 1L;
 186       long totalNumClasses = bootstrapLoaderData.numClasses;
 187       long totalClassSize  = bootstrapLoaderData.classSize;
 188       long numAliveLoaders = 1L;
 189       long numDeadLoaders  = 0L;
 190 
 191       // print bootstrap loader details
 192       out.print("<bootstrap>");
 193       out.print('\t');
 194       out.print(bootstrapLoaderData.numClasses);
 195       out.print('\t');
 196       out.print(bootstrapLoaderData.classSize);
 197       out.print('\t');
 198       out.print("  null  ");
 199       out.print('\t');
 200       // bootstrap loader is always alive
 201       out.print("live");
 202       out.print('\t');
 203       out.println("<internal>");
 204 
 205       for (Iterator keyItr = loaderMap.keySet().iterator(); keyItr.hasNext();) {
 206          Oop loader = (Oop) keyItr.next();
 207          LoaderData data = (LoaderData) loaderMap.get(loader);
 208          numClassLoaders ++;
 209          totalNumClasses += data.numClasses;
 210          totalClassSize  += data.classSize;
 211 
 212          out.print(loader.getHandle());
 213          out.print('\t');
 214          out.print(data.numClasses);
 215          out.print('\t');
 216          out.print(data.classSize);
 217          out.print('\t');
 218 
 219          class ParentFinder extends DefaultOopVisitor {
 220             public void doOop(OopField field, boolean isVMField) {
 221                if (field.getID().getName().equals("parent")) {
 222                   parent = field.getValue(getObj());
 223                }
 224             }
 225             private Oop parent = null;
 226             public Oop getParent() { return parent; }
 227          }
 228 
 229          ParentFinder parentFinder = new ParentFinder();
 230          loader.iterate(parentFinder, false);
 231          Oop parent = parentFinder.getParent();
 232          out.print((parent != null)? parent.getHandle().toString() : "  null  ");
 233          out.print('\t');
 234          boolean alive = (liveness != null) ? (liveness.get(loader) != null) : true;
 235          out.print(alive? "live" : "dead");
 236          if (alive) numAliveLoaders++; else numDeadLoaders++;
 237          out.print('\t');
 238          Klass loaderKlass = loader.getKlass();
 239          if (loaderKlass != null) {
 240             out.print(loaderKlass.getName().asString());
 241             out.print('@');
 242             out.print(loader.getKlass().getHandle());
 243          } else {
 244             out.print("    null!    ");
 245          }
 246          out.println();
 247       }
 248 
 249       out.println();
 250       // summary line
 251       out.print("total = ");
 252       out.print(numClassLoaders);
 253       out.print('\t');
 254       out.print(totalNumClasses);
 255       out.print('\t');
 256       out.print(totalClassSize);
 257       out.print('\t');
 258       out.print("    N/A    ");
 259       out.print('\t');
 260       out.print("alive=");
 261       out.print(numAliveLoaders);
 262       out.print(", dead=");
 263       out.print(numDeadLoaders); 
 264       out.print('\t');
 265       out.print("    N/A    ");
 266       out.println();
 267    }
 268 
 269    private long computeSize(InstanceKlass k) {
 270       long size = 0L;
 271       // InstanceKlass object size
 272       size += k.getObjectSize();
 273 
 274       // add ConstantPool size
 275       size += k.getConstants().getObjectSize();
 276 
 277       // add ConstantPoolCache, if any
 278       ConstantPoolCache cpCache = k.getConstants().getCache();
 279       if (cpCache != null) {
 280          size += cpCache.getObjectSize();
 281       }
 282 
 283       // add interfaces size
 284       ObjArray interfaces = k.getLocalInterfaces();
 285       size +=  (interfaces.getLength() != 0L)? interfaces.getObjectSize() : 0L;
 286       ObjArray transitiveInterfaces = k.getTransitiveInterfaces();
 287       size += (transitiveInterfaces.getLength() != 0L)? transitiveInterfaces.getObjectSize() : 0L;
 288 
 289       // add inner classes size
 290       TypeArray innerClasses = k.getInnerClasses();
 291       size += innerClasses.getObjectSize();
 292 
 293       // add fields size
 294       size += k.getFields().getObjectSize(); 
 295 
 296       // add methods size
 297       ObjArray methods = k.getMethods();
 298       size += (methods.getLength() != 0L)? methods.getObjectSize() : 0L;
 299       TypeArray methodOrdering = k.getMethodOrdering();
 300       size += (methodOrdering.getLength() != 0L)? methodOrdering.getObjectSize() : 0;
 301 
 302       // add each method's size
 303       int numMethods = (int) methods.getLength();
 304       for (int i = 0; i < numMethods; i++) {
 305          Method m = (Method) methods.getObjAt(i);
 306          size += m.getObjectSize();
 307       }
 308 
 309       return size;
 310    }
 311 }