1 /*
   2  * Copyright (c) 2003, 2013, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * 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.utilities.*;
  35 
  36 /**
  37   A command line tool to print class loader statistics.
  38 */
  39 
  40 public class ClassLoaderStats extends Tool {
  41    boolean verbose = true;
  42 
  43    public ClassLoaderStats() {
  44       super();
  45    }
  46 
  47    public ClassLoaderStats(JVMDebugger d) {
  48       super(d);
  49    }
  50 
  51    @Override
  52    public String getName() {
  53       return "classLoaderStats";
  54    }
  55 
  56    public static void main(String[] args) {
  57       ClassLoaderStats cls = new ClassLoaderStats();
  58       cls.execute(args);
  59    }
  60 
  61    private static class ClassData {
  62       Klass klass;
  63       long  size;
  64 
  65       ClassData(Klass klass, long size) {
  66          this.klass = klass; this.size = size;
  67       }
  68    }
  69 
  70    private static class LoaderData {
  71       long     numClasses;
  72       long     classSize;
  73       List     classDetail = new ArrayList(); // List<ClassData>
  74    }
  75 
  76    public void run() {
  77       printClassLoaderStatistics();
  78    }
  79 
  80    private void printClassLoaderStatistics() {
  81       final PrintStream out = System.out;
  82       final PrintStream err = System.err;
  83       final Map loaderMap = new HashMap();
  84       // loader data for bootstrap class loader
  85       final LoaderData bootstrapLoaderData = new LoaderData();
  86       if (verbose) {
  87          err.print("finding class loader instances ..");
  88       }
  89 
  90       VM vm = VM.getVM();
  91       ObjectHeap heap = vm.getObjectHeap();
  92       Klass classLoaderKlass = vm.getSystemDictionary().getClassLoaderKlass();
  93       try {
  94          heap.iterateObjectsOfKlass(new DefaultHeapVisitor() {
  95                          public boolean doObj(Oop oop) {
  96                             loaderMap.put(oop, new LoaderData());
  97                                                         return false;
  98                          }
  99                       }, classLoaderKlass);
 100       } catch (Exception se) {
 101          se.printStackTrace();
 102       }
 103 
 104       if (verbose) {
 105          err.println("done.");
 106          err.print("computing per loader stat ..");
 107       }
 108 
 109       SystemDictionary dict = VM.getVM().getSystemDictionary();
 110       dict.classesDo(new SystemDictionary.ClassVisitor() {
 111                         public void visit(Klass k) {
 112                            if (! (k instanceof InstanceKlass)) {
 113                               return;
 114                            }
 115                            Oop loader = ((InstanceKlass) k).getClassLoader();
 116                            LoaderData ld = (loader != null) ? (LoaderData)loaderMap.get(loader)
 117                                                             : bootstrapLoaderData;
 118                            if (ld != null) {
 119                               ld.numClasses++;
 120                               long size = computeSize((InstanceKlass)k);
 121                               ld.classDetail.add(new ClassData(k, size));
 122                               ld.classSize += size;
 123                            }
 124                         }
 125                      });
 126 
 127       if (verbose) {
 128          err.println("done.");
 129          err.print("please wait.. computing liveness");
 130       }
 131 
 132       // compute reverse pointer analysis (takes long time for larger app)
 133       ReversePtrsAnalysis analysis = new ReversePtrsAnalysis();
 134 
 135       if (verbose) {
 136          analysis.setHeapProgressThunk(new HeapProgressThunk() {
 137             public void heapIterationFractionUpdate(double fractionOfHeapVisited) {
 138                err.print('.');
 139             }
 140             // This will be called after the iteration is complete
 141             public void heapIterationComplete() {
 142                err.println("done.");
 143             }
 144          });
 145       }
 146 
 147       try {
 148          analysis.run();
 149       } catch (Exception e) {
 150          // e.printStackTrace();
 151          if (verbose)
 152            err.println("liveness analysis may be inaccurate ...");
 153       }
 154       ReversePtrs liveness = VM.getVM().getRevPtrs();
 155 
 156       out.println("class_loader\tclasses\tbytes\tparent_loader\talive?\ttype");
 157       out.println();
 158 
 159       long numClassLoaders = 1L;
 160       long totalNumClasses = bootstrapLoaderData.numClasses;
 161       long totalClassSize  = bootstrapLoaderData.classSize;
 162       long numAliveLoaders = 1L;
 163       long numDeadLoaders  = 0L;
 164 
 165       // print bootstrap loader details
 166       out.print("<bootstrap>");
 167       out.print('\t');
 168       out.print(bootstrapLoaderData.numClasses);
 169       out.print('\t');
 170       out.print(bootstrapLoaderData.classSize);
 171       out.print('\t');
 172       out.print("  null  ");
 173       out.print('\t');
 174       // bootstrap loader is always alive
 175       out.print("live");
 176       out.print('\t');
 177       out.println("<internal>");
 178 
 179       for (Iterator keyItr = loaderMap.keySet().iterator(); keyItr.hasNext();) {
 180          Oop loader = (Oop) keyItr.next();
 181          LoaderData data = (LoaderData) loaderMap.get(loader);
 182          numClassLoaders ++;
 183          totalNumClasses += data.numClasses;
 184          totalClassSize  += data.classSize;
 185 
 186          out.print(loader.getHandle());
 187          out.print('\t');
 188          out.print(data.numClasses);
 189          out.print('\t');
 190          out.print(data.classSize);
 191          out.print('\t');
 192 
 193          class ParentFinder extends DefaultOopVisitor {
 194             public void doOop(OopField field, boolean isVMField) {
 195                if (field.getID().getName().equals("parent")) {
 196                   parent = field.getValue(getObj());
 197                }
 198             }
 199             private Oop parent = null;
 200             public Oop getParent() { return parent; }
 201          }
 202 
 203          ParentFinder parentFinder = new ParentFinder();
 204          loader.iterate(parentFinder, false);
 205          Oop parent = parentFinder.getParent();
 206          out.print((parent != null)? parent.getHandle().toString() : "  null  ");
 207          out.print('\t');
 208          boolean alive = (liveness != null) ? (liveness.get(loader) != null) : true;
 209          out.print(alive? "live" : "dead");
 210          if (alive) numAliveLoaders++; else numDeadLoaders++;
 211          out.print('\t');
 212          Klass loaderKlass = loader.getKlass();
 213          if (loaderKlass != null) {
 214             out.print(loaderKlass.getName().asString());
 215             out.print('@');
 216             out.print(loader.getKlass().getAddress());
 217          } else {
 218             out.print("    null!    ");
 219          }
 220          out.println();
 221       }
 222 
 223       out.println();
 224       // summary line
 225       out.print("total = ");
 226       out.print(numClassLoaders);
 227       out.print('\t');
 228       out.print(totalNumClasses);
 229       out.print('\t');
 230       out.print(totalClassSize);
 231       out.print('\t');
 232       out.print("    N/A    ");
 233       out.print('\t');
 234       out.print("alive=");
 235       out.print(numAliveLoaders);
 236       out.print(", dead=");
 237       out.print(numDeadLoaders);
 238       out.print('\t');
 239       out.print("    N/A    ");
 240       out.println();
 241    }
 242 
 243    private static long objectSize(Oop oop) {
 244       return oop == null ? 0L : oop.getObjectSize();
 245    }
 246 
 247    // Don't count the shared empty arrays
 248    private static long arraySize(GenericArray arr) {
 249      return arr.getLength() != 0L ? arr.getSize() : 0L;
 250    }
 251 
 252    private long computeSize(InstanceKlass k) {
 253       long size = 0L;
 254       // the InstanceKlass object itself
 255       size += k.getSize();
 256 
 257       // Constant pool
 258       ConstantPool cp = k.getConstants();
 259       size += cp.getSize();
 260       if (cp.getCache() != null) {
 261         size += cp.getCache().getSize();
 262       }
 263       size += arraySize(cp.getTags());
 264 
 265       // Interfaces
 266       size += arraySize(k.getLocalInterfaces());
 267       size += arraySize(k.getTransitiveInterfaces());
 268 
 269       // Inner classes
 270       size += arraySize(k.getInnerClasses());
 271 
 272       // Fields
 273       size += arraySize(k.getFields());
 274 
 275       // Methods
 276       MethodArray methods = k.getMethods();
 277       int nmethods = (int) methods.getLength();
 278       if (nmethods != 0L) {
 279          size += methods.getSize();
 280          for (int i = 0; i < nmethods; ++i) {
 281             Method m = methods.at(i);
 282             size += m.getSize();
 283             size += m.getConstMethod().getSize();
 284          }
 285       }
 286 
 287       return size;
 288    }
 289 }