/* * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. * */ package sun.jvm.hotspot.tools; import java.io.*; import java.util.*; import sun.jvm.hotspot.debugger.*; import sun.jvm.hotspot.memory.*; import sun.jvm.hotspot.oops.*; import sun.jvm.hotspot.runtime.*; import sun.jvm.hotspot.tools.*; import sun.jvm.hotspot.utilities.*; /** A command line tool to print perm. generation statistics. */ public class PermStat extends Tool { boolean verbose = true; public static void main(String[] args) { PermStat ps = new PermStat(); ps.start(args); ps.stop(); } private static class ClassData { Klass klass; long size; ClassData(Klass klass, long size) { this.klass = klass; this.size = size; } } private static class LoaderData { long numClasses; long classSize; List classDetail = new ArrayList(); // List } public void run() { printInternStringStatistics(); printClassLoaderStatistics(); } private void printInternStringStatistics() { class StringStat implements StringTable.StringVisitor { private int count; private long size; private OopField stringValueField; StringStat() { VM vm = VM.getVM(); SystemDictionary sysDict = vm.getSystemDictionary(); InstanceKlass strKlass = sysDict.getStringKlass(); // String has a field named 'value' of type 'char[]'. stringValueField = (OopField) strKlass.findField("value", "[C"); } private long stringSize(Instance instance) { // We include String content in size calculation. return instance.getObjectSize() + stringValueField.getValue(instance).getObjectSize(); } public void visit(Instance str) { count++; size += stringSize(str); } public void print() { System.out.println(count + " intern Strings occupying " + size + " bytes."); } } StringStat stat = new StringStat(); StringTable strTable = VM.getVM().getStringTable(); strTable.stringsDo(stat); stat.print(); } private void printClassLoaderStatistics() { final PrintStream out = System.out; final PrintStream err = System.err; final Map loaderMap = new HashMap(); // loader data for bootstrap class loader final LoaderData bootstrapLoaderData = new LoaderData(); if (verbose) { err.print("finding class loader instances .."); } VM vm = VM.getVM(); ObjectHeap heap = vm.getObjectHeap(); Klass classLoaderKlass = vm.getSystemDictionary().getClassLoaderKlass(); try { heap.iterateObjectsOfKlass(new DefaultHeapVisitor() { public boolean doObj(Oop oop) { loaderMap.put(oop, new LoaderData()); return false; } }, classLoaderKlass); } catch (Exception se) { se.printStackTrace(); } if (verbose) { err.println("done."); err.print("computing per loader stat .."); } SystemDictionary dict = VM.getVM().getSystemDictionary(); dict.classesDo(new SystemDictionary.ClassAndLoaderVisitor() { public void visit(Klass k, Oop loader) { if (! (k instanceof InstanceKlass)) { return; } LoaderData ld = (loader != null) ? (LoaderData)loaderMap.get(loader) : bootstrapLoaderData; if (ld != null) { ld.numClasses++; long size = computeSize((InstanceKlass)k); ld.classDetail.add(new ClassData(k, size)); ld.classSize += size; } } }); if (verbose) { err.println("done."); err.print("please wait.. computing liveness"); } // compute reverse pointer analysis (takes long time for larger app) ReversePtrsAnalysis analysis = new ReversePtrsAnalysis(); if (verbose) { analysis.setHeapProgressThunk(new HeapProgressThunk() { public void heapIterationFractionUpdate(double fractionOfHeapVisited) { err.print('.'); } // This will be called after the iteration is complete public void heapIterationComplete() { err.println("done."); } }); } try { analysis.run(); } catch (Exception e) { // e.printStackTrace(); if (verbose) err.println("liveness analysis may be inaccurate ..."); } ReversePtrs liveness = VM.getVM().getRevPtrs(); out.println("class_loader\tclasses\tbytes\tparent_loader\talive?\ttype"); out.println(); long numClassLoaders = 1L; long totalNumClasses = bootstrapLoaderData.numClasses; long totalClassSize = bootstrapLoaderData.classSize; long numAliveLoaders = 1L; long numDeadLoaders = 0L; // print bootstrap loader details out.print(""); out.print('\t'); out.print(bootstrapLoaderData.numClasses); out.print('\t'); out.print(bootstrapLoaderData.classSize); out.print('\t'); out.print(" null "); out.print('\t'); // bootstrap loader is always alive out.print("live"); out.print('\t'); out.println(""); for (Iterator keyItr = loaderMap.keySet().iterator(); keyItr.hasNext();) { Oop loader = (Oop) keyItr.next(); LoaderData data = (LoaderData) loaderMap.get(loader); numClassLoaders ++; totalNumClasses += data.numClasses; totalClassSize += data.classSize; out.print(loader.getHandle()); out.print('\t'); out.print(data.numClasses); out.print('\t'); out.print(data.classSize); out.print('\t'); class ParentFinder extends DefaultOopVisitor { public void doOop(OopField field, boolean isVMField) { if (field.getID().getName().equals("parent")) { parent = field.getValue(getObj()); } } private Oop parent = null; public Oop getParent() { return parent; } } ParentFinder parentFinder = new ParentFinder(); loader.iterate(parentFinder, false); Oop parent = parentFinder.getParent(); out.print((parent != null)? parent.getHandle().toString() : " null "); out.print('\t'); boolean alive = (liveness != null) ? (liveness.get(loader) != null) : true; out.print(alive? "live" : "dead"); if (alive) numAliveLoaders++; else numDeadLoaders++; out.print('\t'); Klass loaderKlass = loader.getKlass(); if (loaderKlass != null) { out.print(loaderKlass.getName().asString()); out.print('@'); out.print(loader.getKlass().getHandle()); } else { out.print(" null! "); } out.println(); } out.println(); // summary line out.print("total = "); out.print(numClassLoaders); out.print('\t'); out.print(totalNumClasses); out.print('\t'); out.print(totalClassSize); out.print('\t'); out.print(" N/A "); out.print('\t'); out.print("alive="); out.print(numAliveLoaders); out.print(", dead="); out.print(numDeadLoaders); out.print('\t'); out.print(" N/A "); out.println(); } private static long objectSize(Oop oop) { return oop == null ? 0L : oop.getObjectSize(); } // Don't count the shared empty arrays private static long arraySize(Array arr) { return arr.getLength() != 0L ? arr.getObjectSize() : 0L; } private long computeSize(InstanceKlass k) { long size = 0L; // the InstanceKlass object itself size += k.getObjectSize(); // Constant pool ConstantPool cp = k.getConstants(); size += cp.getObjectSize(); size += objectSize(cp.getCache()); size += objectSize(cp.getTags()); // Interfaces size += arraySize(k.getLocalInterfaces()); size += arraySize(k.getTransitiveInterfaces()); // Inner classes size += objectSize(k.getInnerClasses()); // Fields size += objectSize(k.getFields()); // Methods ObjArray methods = k.getMethods(); int nmethods = (int) methods.getLength(); if (nmethods != 0L) { size += methods.getObjectSize(); for (int i = 0; i < nmethods; ++i) { Method m = (Method) methods.getObjAt(i); size += m.getObjectSize(); size += objectSize(m.getConstMethod()); } } // MethodOrdering - an int array that records the original // ordering of methods in the class file size += arraySize(k.getMethodOrdering()); return size; } }