--- /dev/null 2013-02-19 18:27:51.183525753 +0100 +++ new/agent/src/share/classes/sun/jvm/hotspot/tools/ClassLoaderStats.java 2013-02-20 16:26:23.020745364 +0100 @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.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 class loader statistics. +*/ + +public class ClassLoaderStats extends Tool { + boolean verbose = true; + + public static void main(String[] args) { + ClassLoaderStats cls = new ClassLoaderStats(); + cls.start(args); + cls.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() { + printClassLoaderStatistics(); + } + + 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().getAddress()); + } 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(GenericArray arr) { + return arr.getLength() != 0L ? arr.getSize() : 0L; + } + + private long computeSize(InstanceKlass k) { + long size = 0L; + // the InstanceKlass object itself + size += k.getSize(); + + // Constant pool + ConstantPool cp = k.getConstants(); + size += cp.getSize(); + if (cp.getCache() != null) { + size += cp.getCache().getSize(); + } + size += arraySize(cp.getTags()); + + // Interfaces + size += arraySize(k.getLocalInterfaces()); + size += arraySize(k.getTransitiveInterfaces()); + + // Inner classes + size += arraySize(k.getInnerClasses()); + + // Fields + size += arraySize(k.getFields()); + + // Methods + MethodArray methods = k.getMethods(); + int nmethods = (int) methods.getLength(); + if (nmethods != 0L) { + size += methods.getSize(); + for (int i = 0; i < nmethods; ++i) { + Method m = methods.at(i); + size += m.getSize(); + size += m.getConstMethod().getSize(); + } + } + + return size; + } +}