| /* |
| * Copyright (c) 2003, 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 ClassLoaderStats() { |
| super(); |
| } |
| |
| public ClassLoaderStats(JVMDebugger d) { |
| super(d); |
| } |
| |
| public static void main(String[] args) { |
| ClassLoaderStats cls = new ClassLoaderStats(); |
| cls.execute(args); |
| } |
| |
| 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() { |
| 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("<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().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; |
| } |
| } |