blob: 1808babf2ccbcca5df3adec64cfedeccf990892e [file] [log] [blame]
/*
* Copyright 2003-2006 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<ClassData>
}
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("<bootstrap>");
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("<internal>");
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 long computeSize(InstanceKlass k) {
long size = 0L;
// InstanceKlass object size
size += k.getObjectSize();
// add ConstantPool size
size += k.getConstants().getObjectSize();
// add ConstantPoolCache, if any
ConstantPoolCache cpCache = k.getConstants().getCache();
if (cpCache != null) {
size += cpCache.getObjectSize();
}
// add interfaces size
ObjArray interfaces = k.getLocalInterfaces();
size += (interfaces.getLength() != 0L)? interfaces.getObjectSize() : 0L;
ObjArray transitiveInterfaces = k.getTransitiveInterfaces();
size += (transitiveInterfaces.getLength() != 0L)? transitiveInterfaces.getObjectSize() : 0L;
// add inner classes size
TypeArray innerClasses = k.getInnerClasses();
size += innerClasses.getObjectSize();
// add fields size
size += k.getFields().getObjectSize();
// add methods size
ObjArray methods = k.getMethods();
size += (methods.getLength() != 0L)? methods.getObjectSize() : 0L;
TypeArray methodOrdering = k.getMethodOrdering();
size += (methodOrdering.getLength() != 0L)? methodOrdering.getObjectSize() : 0;
// add each method's size
int numMethods = (int) methods.getLength();
for (int i = 0; i < numMethods; i++) {
Method m = (Method) methods.getObjAt(i);
size += m.getObjectSize();
}
return size;
}
}