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 }