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 }