1 /* 2 * Copyright 2003-2006 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 20 * CA 95054 USA or visit www.sun.com if you need additional information or 21 * have any 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 perm. generation statistics. 39 */ 40 41 public class PermStat extends Tool { 42 boolean verbose = true; 43 44 public static void main(String[] args) { 45 PermStat ps = new PermStat(); 46 ps.start(args); 47 ps.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 printInternStringStatistics(); 67 printClassLoaderStatistics(); 68 } 69 70 private void printInternStringStatistics() { 71 class StringStat implements StringTable.StringVisitor { 72 private int count; 73 private long size; 74 private OopField stringValueField; 75 76 StringStat() { 77 VM vm = VM.getVM(); 78 SystemDictionary sysDict = vm.getSystemDictionary(); 79 InstanceKlass strKlass = sysDict.getStringKlass(); 80 // String has a field named 'value' of type 'char[]'. 81 stringValueField = (OopField) strKlass.findField("value", "[C"); 82 } 83 84 private long stringSize(Instance instance) { 85 // We include String content in size calculation. 86 return instance.getObjectSize() + 87 stringValueField.getValue(instance).getObjectSize(); 88 } 89 90 public void visit(Instance str) { 91 count++; 92 size += stringSize(str); 93 } 94 95 public void print() { 96 System.out.println(count + 97 " intern Strings occupying " + size + " bytes."); 98 } 99 } 100 101 StringStat stat = new StringStat(); 102 StringTable strTable = VM.getVM().getStringTable(); 103 strTable.stringsDo(stat); 104 stat.print(); 105 } 106 107 private void printClassLoaderStatistics() { 108 final PrintStream out = System.out; 109 final PrintStream err = System.err; 110 final Map loaderMap = new HashMap(); 111 // loader data for bootstrap class loader 112 final LoaderData bootstrapLoaderData = new LoaderData(); 113 if (verbose) { 114 err.print("finding class loader instances .."); 115 } 116 117 VM vm = VM.getVM(); 118 ObjectHeap heap = vm.getObjectHeap(); 119 Klass classLoaderKlass = vm.getSystemDictionary().getClassLoaderKlass(); 120 try { 121 heap.iterateObjectsOfKlass(new DefaultHeapVisitor() { 122 public boolean doObj(Oop oop) { 123 loaderMap.put(oop, new LoaderData()); 124 return false; 125 } 126 }, classLoaderKlass); 127 } catch (Exception se) { 128 se.printStackTrace(); 129 } 130 131 if (verbose) { 132 err.println("done."); 133 err.print("computing per loader stat .."); 134 } 135 136 SystemDictionary dict = VM.getVM().getSystemDictionary(); 137 dict.classesDo(new SystemDictionary.ClassAndLoaderVisitor() { 138 public void visit(Klass k, Oop loader) { 139 if (! (k instanceof InstanceKlass)) { 140 return; 141 } 142 LoaderData ld = (loader != null) ? (LoaderData)loaderMap.get(loader) 143 : bootstrapLoaderData; 144 if (ld != null) { 145 ld.numClasses++; 146 long size = computeSize((InstanceKlass)k); 147 ld.classDetail.add(new ClassData(k, size)); 148 ld.classSize += size; 149 } 150 } 151 }); 152 153 if (verbose) { 154 err.println("done."); 155 err.print("please wait.. computing liveness"); 156 } 157 158 // compute reverse pointer analysis (takes long time for larger app) 159 ReversePtrsAnalysis analysis = new ReversePtrsAnalysis(); 160 161 if (verbose) { 162 analysis.setHeapProgressThunk(new HeapProgressThunk() { 163 public void heapIterationFractionUpdate(double fractionOfHeapVisited) { 164 err.print('.'); 165 } 166 // This will be called after the iteration is complete 167 public void heapIterationComplete() { 168 err.println("done."); 169 } 170 }); 171 } 172 173 try { 174 analysis.run(); 175 } catch (Exception e) { 176 // e.printStackTrace(); 177 if (verbose) 178 err.println("liveness analysis may be inaccurate ..."); 179 } 180 ReversePtrs liveness = VM.getVM().getRevPtrs(); 181 182 out.println("class_loader\tclasses\tbytes\tparent_loader\talive?\ttype"); 183 out.println(); 184 185 long numClassLoaders = 1L; 186 long totalNumClasses = bootstrapLoaderData.numClasses; 187 long totalClassSize = bootstrapLoaderData.classSize; 188 long numAliveLoaders = 1L; 189 long numDeadLoaders = 0L; 190 191 // print bootstrap loader details 192 out.print("<bootstrap>"); 193 out.print('\t'); 194 out.print(bootstrapLoaderData.numClasses); 195 out.print('\t'); 196 out.print(bootstrapLoaderData.classSize); 197 out.print('\t'); 198 out.print(" null "); 199 out.print('\t'); 200 // bootstrap loader is always alive 201 out.print("live"); 202 out.print('\t'); 203 out.println("<internal>"); 204 205 for (Iterator keyItr = loaderMap.keySet().iterator(); keyItr.hasNext();) { 206 Oop loader = (Oop) keyItr.next(); 207 LoaderData data = (LoaderData) loaderMap.get(loader); 208 numClassLoaders ++; 209 totalNumClasses += data.numClasses; 210 totalClassSize += data.classSize; 211 212 out.print(loader.getHandle()); 213 out.print('\t'); 214 out.print(data.numClasses); 215 out.print('\t'); 216 out.print(data.classSize); 217 out.print('\t'); 218 219 class ParentFinder extends DefaultOopVisitor { 220 public void doOop(OopField field, boolean isVMField) { 221 if (field.getID().getName().equals("parent")) { 222 parent = field.getValue(getObj()); 223 } 224 } 225 private Oop parent = null; 226 public Oop getParent() { return parent; } 227 } 228 229 ParentFinder parentFinder = new ParentFinder(); 230 loader.iterate(parentFinder, false); 231 Oop parent = parentFinder.getParent(); 232 out.print((parent != null)? parent.getHandle().toString() : " null "); 233 out.print('\t'); 234 boolean alive = (liveness != null) ? (liveness.get(loader) != null) : true; 235 out.print(alive? "live" : "dead"); 236 if (alive) numAliveLoaders++; else numDeadLoaders++; 237 out.print('\t'); 238 Klass loaderKlass = loader.getKlass(); 239 if (loaderKlass != null) { 240 out.print(loaderKlass.getName().asString()); 241 out.print('@'); 242 out.print(loader.getKlass().getHandle()); 243 } else { 244 out.print(" null! "); 245 } 246 out.println(); 247 } 248 249 out.println(); 250 // summary line 251 out.print("total = "); 252 out.print(numClassLoaders); 253 out.print('\t'); 254 out.print(totalNumClasses); 255 out.print('\t'); 256 out.print(totalClassSize); 257 out.print('\t'); 258 out.print(" N/A "); 259 out.print('\t'); 260 out.print("alive="); 261 out.print(numAliveLoaders); 262 out.print(", dead="); 263 out.print(numDeadLoaders); 264 out.print('\t'); 265 out.print(" N/A "); 266 out.println(); 267 } 268 269 private long computeSize(InstanceKlass k) { 270 long size = 0L; 271 // InstanceKlass object size 272 size += k.getObjectSize(); 273 274 // add ConstantPool size 275 size += k.getConstants().getObjectSize(); 276 277 // add ConstantPoolCache, if any 278 ConstantPoolCache cpCache = k.getConstants().getCache(); 279 if (cpCache != null) { 280 size += cpCache.getObjectSize(); 281 } 282 283 // add interfaces size 284 ObjArray interfaces = k.getLocalInterfaces(); 285 size += (interfaces.getLength() != 0L)? interfaces.getObjectSize() : 0L; 286 ObjArray transitiveInterfaces = k.getTransitiveInterfaces(); 287 size += (transitiveInterfaces.getLength() != 0L)? transitiveInterfaces.getObjectSize() : 0L; 288 289 // add inner classes size 290 TypeArray innerClasses = k.getInnerClasses(); 291 size += innerClasses.getObjectSize(); 292 293 // add fields size 294 size += k.getFields().getObjectSize(); 295 296 // add methods size 297 ObjArray methods = k.getMethods(); 298 size += (methods.getLength() != 0L)? methods.getObjectSize() : 0L; 299 TypeArray methodOrdering = k.getMethodOrdering(); 300 size += (methodOrdering.getLength() != 0L)? methodOrdering.getObjectSize() : 0; 301 302 // add each method's size 303 int numMethods = (int) methods.getLength(); 304 for (int i = 0; i < numMethods; i++) { 305 Method m = (Method) methods.getObjAt(i); 306 size += m.getObjectSize(); 307 } 308 309 return size; 310 } 311 }