1 /* 2 * Copyright 2003-2008 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 static long objectSize(Oop oop) { 270 return oop == null ? 0L : oop.getObjectSize(); 271 } 272 273 // Don't count the shared empty arrays 274 private static long arraySize(Array arr) { 275 return arr.getLength() != 0L ? arr.getObjectSize() : 0L; 276 } 277 278 private long computeSize(InstanceKlass k) { 279 long size = 0L; 280 // the InstanceKlass object itself 281 size += k.getObjectSize(); 282 283 // Constant pool 284 ConstantPool cp = k.getConstants(); 285 size += cp.getObjectSize(); 286 size += objectSize(cp.getCache()); 287 size += objectSize(cp.getTags()); 288 289 // Interfaces 290 size += arraySize(k.getLocalInterfaces()); 291 size += arraySize(k.getTransitiveInterfaces()); 292 293 // Inner classes 294 size += objectSize(k.getInnerClasses()); 295 296 // Fields 297 size += objectSize(k.getFields()); 298 299 // Methods 300 ObjArray methods = k.getMethods(); 301 int nmethods = (int) methods.getLength(); 302 if (nmethods != 0L) { 303 size += methods.getObjectSize(); 304 for (int i = 0; i < nmethods; ++i) { 305 Method m = (Method) methods.getObjAt(i); 306 size += m.getObjectSize(); 307 size += objectSize(m.getConstMethod()); 308 } 309 } 310 311 // MethodOrdering - an int array that records the original 312 // ordering of methods in the class file 313 size += arraySize(k.getMethodOrdering()); 314 315 return size; 316 } 317 }