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