8044107: Add Diagnostic Command to list all ClassLoaders
Reviewed-by: mgerdin, stefank
--- a/hotspot/src/share/vm/classfile/classLoaderData.cpp Mon Jun 02 17:21:15 2014 +0200
+++ b/hotspot/src/share/vm/classfile/classLoaderData.cpp Wed Jun 04 11:05:30 2014 +0200
@@ -624,6 +624,12 @@
}
}
+void ClassLoaderDataGraph::cld_do(CLDClosure* cl) {
+ for (ClassLoaderData* cld = _head; cl != NULL && cld != NULL; cld = cld->next()) {
+ cl->do_cld(cld);
+ }
+}
+
void ClassLoaderDataGraph::classes_do(KlassClosure* klass_closure) {
for (ClassLoaderData* cld = _head; cld != NULL; cld = cld->next()) {
cld->classes_do(klass_closure);
--- a/hotspot/src/share/vm/classfile/classLoaderData.hpp Mon Jun 02 17:21:15 2014 +0200
+++ b/hotspot/src/share/vm/classfile/classLoaderData.hpp Wed Jun 04 11:05:30 2014 +0200
@@ -77,6 +77,7 @@
static void oops_do(OopClosure* f, KlassClosure* klass_closure, bool must_claim);
static void always_strong_oops_do(OopClosure* blk, KlassClosure* klass_closure, bool must_claim);
static void keep_alive_oops_do(OopClosure* blk, KlassClosure* klass_closure, bool must_claim);
+ static void cld_do(CLDClosure* cl);
static void classes_do(KlassClosure* klass_closure);
static void classes_do(void f(Klass* const));
static void methods_do(void f(Method*));
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/classfile/classLoaderStats.cpp Wed Jun 04 11:05:30 2014 +0200
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "classfile/classLoaderStats.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+
+class ClassStatsClosure : public KlassClosure {
+public:
+ int _num_classes;
+
+ ClassStatsClosure() :
+ _num_classes(0) {
+ }
+
+ virtual void do_klass(Klass* k) {
+ _num_classes++;
+ }
+};
+
+
+void ClassLoaderStatsClosure::do_cld(ClassLoaderData* cld) {
+ oop cl = cld->class_loader();
+ ClassLoaderStats* cls;
+
+ // The hashtable key is the ClassLoader oop since we want to account
+ // for "real" classes and anonymous classes together
+ ClassLoaderStats** cls_ptr = _stats->get(cl);
+ if (cls_ptr == NULL) {
+ cls = new ClassLoaderStats();
+ _stats->put(cl, cls);
+ _total_loaders++;
+ } else {
+ cls = *cls_ptr;
+ }
+
+ if (!cld->is_anonymous()) {
+ cls->_cld = cld;
+ }
+
+ cls->_class_loader = cl;
+ if (cl != NULL) {
+ cls->_parent = java_lang_ClassLoader::parent(cl);
+ addEmptyParents(cls->_parent);
+ }
+
+ ClassStatsClosure csc;
+ cld->classes_do(&csc);
+ if(cld->is_anonymous()) {
+ cls->_anon_classes_count += csc._num_classes;
+ } else {
+ cls->_classes_count = csc._num_classes;
+ }
+ _total_classes += csc._num_classes;
+
+ Metaspace* ms = cld->metaspace_or_null();
+ if (ms != NULL) {
+ if(cld->is_anonymous()) {
+ cls->_anon_chunk_sz += ms->allocated_chunks_bytes();
+ cls->_anon_block_sz += ms->allocated_blocks_bytes();
+ } else {
+ cls->_chunk_sz = ms->allocated_chunks_bytes();
+ cls->_block_sz = ms->allocated_blocks_bytes();
+ }
+ _total_chunk_sz += ms->allocated_chunks_bytes();
+ _total_block_sz += ms->allocated_blocks_bytes();
+ }
+}
+
+
+// Handles the difference in pointer width on 32 and 64 bit platforms
+#ifdef _LP64
+ #define SPACE "%8s"
+#else
+ #define SPACE "%s"
+#endif
+
+
+bool ClassLoaderStatsClosure::do_entry(oop const& key, ClassLoaderStats* const& cls) {
+ Klass* class_loader_klass = (cls->_class_loader == NULL ? NULL : cls->_class_loader->klass());
+ Klass* parent_klass = (cls->_parent == NULL ? NULL : cls->_parent->klass());
+
+ _out->print(INTPTR_FORMAT " " INTPTR_FORMAT " " INTPTR_FORMAT " " UINTX_FORMAT_W(6) " " SIZE_FORMAT_W(8) " " SIZE_FORMAT_W(8) " ",
+ p2i(class_loader_klass), p2i(parent_klass), p2i(cls->_cld),
+ cls->_classes_count,
+ cls->_chunk_sz, cls->_block_sz);
+ if (class_loader_klass != NULL) {
+ _out->print("%s", class_loader_klass->external_name());
+ } else {
+ _out->print("<boot class loader>");
+ }
+ _out->cr();
+ if (cls->_anon_classes_count > 0) {
+ _out->print_cr(SPACE SPACE SPACE " " UINTX_FORMAT_W(6) " " SIZE_FORMAT_W(8) " " SIZE_FORMAT_W(8) " + unsafe anonymous classes",
+ "", "", "",
+ cls->_anon_classes_count,
+ cls->_anon_chunk_sz, cls->_anon_block_sz);
+ }
+ return true;
+}
+
+
+void ClassLoaderStatsClosure::print() {
+ _out->print_cr("ClassLoader" SPACE " Parent" SPACE " CLD*" SPACE " Classes ChunkSz BlockSz Type", "", "", "");
+ _stats->iterate(this);
+ _out->print("Total = " UINTX_FORMAT_W(-6), _total_loaders);
+ _out->print(SPACE SPACE SPACE " ", "", "", "");
+ _out->print_cr(UINTX_FORMAT_W(6) " " SIZE_FORMAT_W(8) " " SIZE_FORMAT_W(8) " ",
+ _total_classes,
+ _total_chunk_sz,
+ _total_block_sz);
+ _out->print_cr("ChunkSz: Total size of all allocated metaspace chunks");
+ _out->print_cr("BlockSz: Total size of all allocated metaspace blocks (each chunk has several blocks)");
+}
+
+
+void ClassLoaderStatsClosure::addEmptyParents(oop cl) {
+ while (cl != NULL && java_lang_ClassLoader::loader_data(cl) == NULL) {
+ // This classloader has not loaded any classes
+ ClassLoaderStats** cls_ptr = _stats->get(cl);
+ if (cls_ptr == NULL) {
+ // It does not exist in our table - add it
+ ClassLoaderStats* cls = new ClassLoaderStats();
+ cls->_class_loader = cl;
+ cls->_parent = java_lang_ClassLoader::parent(cl);
+ _stats->put(cl, cls);
+ _total_loaders++;
+ }
+
+ cl = java_lang_ClassLoader::parent(cl);
+ }
+}
+
+
+void ClassLoaderStatsVMOperation::doit() {
+ ClassLoaderStatsClosure clsc (_out);
+ ClassLoaderDataGraph::cld_do(&clsc);
+ clsc.print();
+}
+
+
+void ClassLoaderStatsDCmd::execute(DCmdSource source, TRAPS) {
+ ClassLoaderStatsVMOperation op(output());
+ VMThread::execute(&op);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/classfile/classLoaderStats.hpp Wed Jun 04 11:05:30 2014 +0200
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ */
+
+#ifndef SHARE_VM_CLASSFILE_CLASSLOADERSTATS_HPP
+#define SHARE_VM_CLASSFILE_CLASSLOADERSTATS_HPP
+
+
+#include "classfile/classLoaderData.hpp"
+#include "oops/klass.hpp"
+#include "oops/oopsHierarchy.hpp"
+#include "runtime/vm_operations.hpp"
+#include "services/diagnosticCommand.hpp"
+#include "utilities/resourceHash.hpp"
+
+
+class ClassLoaderStatsDCmd : public DCmd {
+public:
+ ClassLoaderStatsDCmd(outputStream* output, bool heap) :
+ DCmd(output, heap) {
+ }
+
+ static const char* name() {
+ return "VM.classloader_stats";
+ }
+
+ static const char* description() {
+ return "Print statistics about all ClassLoaders.";
+ }
+
+ static const char* impact() {
+ return "Low";
+ }
+
+ virtual void execute(DCmdSource source, TRAPS);
+
+ static int num_arguments() {
+ return 0;
+ }
+
+ static const JavaPermission permission() {
+ JavaPermission p = {"java.lang.management.ManagementPermission",
+ "monitor", NULL};
+ return p;
+ }
+};
+
+
+class ClassLoaderStats : public ResourceObj {
+public:
+ ClassLoaderData* _cld;
+ oop _class_loader;
+ oop _parent;
+
+ size_t _chunk_sz;
+ size_t _block_sz;
+ uintx _classes_count;
+
+ size_t _anon_chunk_sz;
+ size_t _anon_block_sz;
+ uintx _anon_classes_count;
+
+ ClassLoaderStats() :
+ _cld(0),
+ _class_loader(0),
+ _parent(0),
+ _chunk_sz(0),
+ _block_sz(0),
+ _classes_count(0),
+ _anon_block_sz(0),
+ _anon_chunk_sz(0),
+ _anon_classes_count(0) {
+ }
+};
+
+
+class ClassLoaderStatsClosure : public CLDClosure {
+protected:
+ static bool oop_equals(oop const& s1, oop const& s2) {
+ return s1 == s2;
+ }
+
+ static unsigned oop_hash(oop const& s1) {
+ unsigned hash = (unsigned)((uintptr_t)&s1);
+ return hash ^ (hash >> LogMinObjAlignment);
+ }
+
+ typedef ResourceHashtable<oop, ClassLoaderStats*,
+ ClassLoaderStatsClosure::oop_hash, ClassLoaderStatsClosure::oop_equals> StatsTable;
+
+ outputStream* _out;
+ StatsTable* _stats;
+ uintx _total_loaders;
+ uintx _total_classes;
+ size_t _total_chunk_sz;
+ size_t _total_block_sz;
+
+public:
+ ClassLoaderStatsClosure(outputStream* out) :
+ _out(out),
+ _total_loaders(0),
+ _total_block_sz(0),
+ _total_chunk_sz(0),
+ _total_classes(0),
+ _stats(new StatsTable()) {
+ }
+
+ virtual void do_cld(ClassLoaderData* cld);
+ virtual bool do_entry(oop const& key, ClassLoaderStats* const& cls);
+ void print();
+
+private:
+ void addEmptyParents(oop cl);
+};
+
+
+class ClassLoaderStatsVMOperation : public VM_Operation {
+ outputStream* _out;
+
+public:
+ ClassLoaderStatsVMOperation(outputStream* out) :
+ _out(out) {
+ }
+
+ VMOp_Type type() const {
+ return VMOp_ClassLoaderStatsOperation;
+ }
+
+ void doit();
+};
+
+#endif // SHARE_VM_CLASSFILE_CLASSLOADERSTATS_HPP
--- a/hotspot/src/share/vm/memory/metaspace.cpp Mon Jun 02 17:21:15 2014 +0200
+++ b/hotspot/src/share/vm/memory/metaspace.cpp Wed Jun 04 11:05:30 2014 +0200
@@ -697,6 +697,7 @@
size_t allocated_blocks_words() const { return _allocated_blocks_words; }
size_t allocated_blocks_bytes() const { return _allocated_blocks_words * BytesPerWord; }
size_t allocated_chunks_words() const { return _allocated_chunks_words; }
+ size_t allocated_chunks_bytes() const { return _allocated_chunks_words * BytesPerWord; }
size_t allocated_chunks_count() const { return _allocated_chunks_count; }
bool is_humongous(size_t word_size) { return word_size > medium_chunk_size(); }
@@ -3349,6 +3350,16 @@
return capacity_words_slow(mdtype) * BytesPerWord;
}
+size_t Metaspace::allocated_blocks_bytes() const {
+ return vsm()->allocated_blocks_bytes() +
+ (using_class_space() ? class_vsm()->allocated_blocks_bytes() : 0);
+}
+
+size_t Metaspace::allocated_chunks_bytes() const {
+ return vsm()->allocated_chunks_bytes() +
+ (using_class_space() ? class_vsm()->allocated_chunks_bytes() : 0);
+}
+
void Metaspace::deallocate(MetaWord* ptr, size_t word_size, bool is_class) {
assert(!SafepointSynchronize::is_at_safepoint()
|| Thread::current()->is_VM_thread(), "should be the VM thread");
--- a/hotspot/src/share/vm/memory/metaspace.hpp Mon Jun 02 17:21:15 2014 +0200
+++ b/hotspot/src/share/vm/memory/metaspace.hpp Wed Jun 04 11:05:30 2014 +0200
@@ -225,6 +225,9 @@
size_t used_bytes_slow(MetadataType mdtype) const;
size_t capacity_bytes_slow(MetadataType mdtype) const;
+ size_t allocated_blocks_bytes() const;
+ size_t allocated_chunks_bytes() const;
+
static MetaWord* allocate(ClassLoaderData* loader_data, size_t word_size,
bool read_only, MetaspaceObj::Type type, TRAPS);
void deallocate(MetaWord* ptr, size_t byte_size, bool is_class);
--- a/hotspot/src/share/vm/runtime/vm_operations.hpp Mon Jun 02 17:21:15 2014 +0200
+++ b/hotspot/src/share/vm/runtime/vm_operations.hpp Wed Jun 04 11:05:30 2014 +0200
@@ -98,6 +98,7 @@
template(LinuxDllLoad) \
template(RotateGCLog) \
template(WhiteBoxOperation) \
+ template(ClassLoaderStatsOperation) \
class VM_Operation: public CHeapObj<mtInternal> {
public:
--- a/hotspot/src/share/vm/services/diagnosticCommand.cpp Mon Jun 02 17:21:15 2014 +0200
+++ b/hotspot/src/share/vm/services/diagnosticCommand.cpp Wed Jun 04 11:05:30 2014 +0200
@@ -23,6 +23,7 @@
*/
#include "precompiled.hpp"
+#include "classfile/classLoaderStats.hpp"
#include "gc_implementation/shared/vmGCOperations.hpp"
#include "runtime/javaCalls.hpp"
#include "runtime/os.hpp"
@@ -58,6 +59,7 @@
#endif // INCLUDE_SERVICES
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ThreadDumpDCmd>(full_export, true, false));
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<RotateGCLogDCmd>(full_export, true, false));
+ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassLoaderStatsDCmd>(full_export, true, false));
// Enhanced JMX Agent Support
// These commands won't be exported via the DiagnosticCommandMBean until an
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/serviceability/dcmd/ClassLoaderStatsTest.java Wed Jun 04 11:05:30 2014 +0200
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+/*
+ * @test
+ *
+ * @build ClassLoaderStatsTest DcmdUtil
+ * @run main ClassLoaderStatsTest
+ */
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.StringReader;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class ClassLoaderStatsTest {
+
+ // ClassLoader Parent CLD* Classes ChunkSz BlockSz Type
+ // 0x00000007c0215928 0x0000000000000000 0x0000000000000000 0 0 0 org.eclipse.osgi.baseadaptor.BaseAdaptor$1
+ // 0x00000007c0009868 0x0000000000000000 0x00007fc52aebcc80 1 6144 3768 sun.reflect.DelegatingClassLoader
+ // 0x00000007c0009868 0x0000000000000000 0x00007fc52b8916d0 1 6144 3688 sun.reflect.DelegatingClassLoader
+ // 0x00000007c0009868 0x00000007c0038ba8 0x00007fc52afb8760 1 6144 3688 sun.reflect.DelegatingClassLoader
+ // 0x00000007c0009868 0x0000000000000000 0x00007fc52afbb1a0 1 6144 3688 sun.reflect.DelegatingClassLoader
+ // 0x0000000000000000 0x0000000000000000 0x00007fc523416070 5019 30060544 29956216 <boot classloader>
+ // 455 1210368 672848 + unsafe anonymous classes
+ // 0x00000007c016b5c8 0x00000007c0038ba8 0x00007fc52a995000 5 8192 5864 org.netbeans.StandardModule$OneModuleClassLoader
+ // 0x00000007c0009868 0x00000007c016b5c8 0x00007fc52ac13640 1 6144 3896 sun.reflect.DelegatingClassLoader
+ // ...
+
+ static Pattern clLine = Pattern.compile("0x\\p{XDigit}*\\s*0x\\p{XDigit}*\\s*0x\\p{XDigit}*\\s*(\\d*)\\s*(\\d*)\\s*(\\d*)\\s*(.*)");
+ static Pattern anonLine = Pattern.compile("\\s*(\\d*)\\s*(\\d*)\\s*(\\d*)\\s*.*");
+
+ public static DummyClassLoader dummyloader;
+
+ public static void main(String arg[]) throws Exception {
+
+ // create a classloader and load our special class
+ dummyloader = new DummyClassLoader();
+ Class<?> c = Class.forName("TestClass", true, dummyloader);
+ if (c.getClassLoader() != dummyloader) {
+ throw new RuntimeException("TestClass defined by wrong classloader: " + c.getClassLoader());
+ }
+
+ String result = DcmdUtil.executeDcmd("VM.classloader_stats");
+ BufferedReader r = new BufferedReader(new StringReader(result));
+ String line;
+ while((line = r.readLine()) != null) {
+ Matcher m = clLine.matcher(line);
+ if (m.matches()) {
+ // verify that DummyClassLoader has loaded 1 class and 1 anonymous class
+ if (m.group(4).equals("ClassLoaderStatsTest$DummyClassLoader")) {
+ System.out.println("line: " + line);
+ if (!m.group(1).equals("1")) {
+ throw new Exception("Should have loaded 1 class: " + line);
+ }
+ checkPositiveInt(m.group(2));
+ checkPositiveInt(m.group(3));
+
+ String next = r.readLine();
+ System.out.println("next: " + next);
+ Matcher m1 = anonLine.matcher(next);
+ m1.matches();
+ if (!m1.group(1).equals("1")) {
+ throw new Exception("Should have loaded 1 anonymous class, but found : " + m1.group(1));
+ }
+ checkPositiveInt(m1.group(2));
+ checkPositiveInt(m1.group(3));
+ }
+ }
+ }
+ }
+
+ private static void checkPositiveInt(String s) throws Exception {
+ if (Integer.parseInt(s) <= 0) {
+ throw new Exception("Value should have been > 0: " + s);
+ }
+ }
+
+ public static class DummyClassLoader extends ClassLoader {
+
+ public static final String CLASS_NAME = "TestClass";
+
+ static ByteBuffer readClassFile(String name)
+ {
+ File f = new File(System.getProperty("test.classes", "."),
+ name);
+ try (FileInputStream fin = new FileInputStream(f);
+ FileChannel fc = fin.getChannel())
+ {
+ return fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
+ } catch (IOException e) {
+ throw new RuntimeException("Can't open file: " + name, e);
+ }
+ }
+
+ protected Class<?> loadClass(String name, boolean resolve)
+ throws ClassNotFoundException
+ {
+ Class<?> c;
+ if (!"TestClass".equals(name)) {
+ c = super.loadClass(name, resolve);
+ } else {
+ // should not delegate to the system class loader
+ c = findClass(name);
+ if (resolve) {
+ resolveClass(c);
+ }
+ }
+ return c;
+ }
+
+ protected Class<?> findClass(String name)
+ throws ClassNotFoundException
+ {
+ if (!"TestClass".equals(name)) {
+ throw new ClassNotFoundException("Unexpected class: " + name);
+ }
+ return defineClass(name, readClassFile(name + ".class"), null);
+ }
+ } /* DummyClassLoader */
+
+}
+
+class TestClass {
+ static {
+ // force creation of anonymous class (for the lambdaform)
+ Runnable r = () -> System.out.println("Hello");
+ r.run();
+ }
+}