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