Created
September 29, 2011 17:25
-
-
Save buzztaiki/1251340 to your computer and use it in GitHub Desktop.
print permgen details
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.*; | |
import java.io.*; | |
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.*; | |
// print permgen details | |
// javac -cp .:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/sa-jdi.jar PermDetail.java | |
// this class is bollowed from sun.jvm.hotspot.tools.PermStat | |
public class PermDetail extends Tool { | |
boolean verbose = true; | |
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<ClassData> classDetail = new ArrayList<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<Oop, LoaderData> loaderMap = new HashMap<Oop, LoaderData>(); | |
// 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 (Oop loader : loaderMap.keySet()) { | |
LoaderData data = 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(); | |
out.println(); | |
out.println("details"); | |
for (Oop loader : loaderMap.keySet()) { | |
Klass loaderKlass = loader.getKlass(); | |
String klassName = (loaderKlass != null ? loaderKlass.getName().asString() : "<null>"); | |
LoaderData data = (LoaderData) loaderMap.get(loader); | |
for (ClassData clazzData : data.classDetail) { | |
out.println(klassName + "@" + loader.getHandle() + "\t" + clazzData.klass.getName().asString() + "\t" + clazzData.size); | |
} | |
} | |
} | |
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; | |
} | |
public static void main(String[] args) throws Exception { | |
PermDetail tool = new PermDetail(); | |
tool.start(args); | |
tool.stop(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment