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