mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
8044107: Add Diagnostic Command to list all ClassLoaders
Reviewed-by: mgerdin, stefank
This commit is contained in:
parent
e563a70ab7
commit
8dc26f1a17
9 changed files with 498 additions and 0 deletions
|
@ -624,6 +624,12 @@ void ClassLoaderDataGraph::always_strong_oops_do(OopClosure* f, KlassClosure* kl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
void ClassLoaderDataGraph::classes_do(KlassClosure* klass_closure) {
|
||||||
for (ClassLoaderData* cld = _head; cld != NULL; cld = cld->next()) {
|
for (ClassLoaderData* cld = _head; cld != NULL; cld = cld->next()) {
|
||||||
cld->classes_do(klass_closure);
|
cld->classes_do(klass_closure);
|
||||||
|
|
|
@ -77,6 +77,7 @@ class ClassLoaderDataGraph : public AllStatic {
|
||||||
static void oops_do(OopClosure* f, KlassClosure* klass_closure, bool must_claim);
|
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 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 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(KlassClosure* klass_closure);
|
||||||
static void classes_do(void f(Klass* const));
|
static void classes_do(void f(Klass* const));
|
||||||
static void methods_do(void f(Method*));
|
static void methods_do(void f(Method*));
|
||||||
|
|
167
hotspot/src/share/vm/classfile/classLoaderStats.cpp
Normal file
167
hotspot/src/share/vm/classfile/classLoaderStats.cpp
Normal file
|
@ -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);
|
||||||
|
}
|
152
hotspot/src/share/vm/classfile/classLoaderStats.hpp
Normal file
152
hotspot/src/share/vm/classfile/classLoaderStats.hpp
Normal file
|
@ -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
|
|
@ -697,6 +697,7 @@ class SpaceManager : public CHeapObj<mtClass> {
|
||||||
size_t allocated_blocks_words() const { return _allocated_blocks_words; }
|
size_t allocated_blocks_words() const { return _allocated_blocks_words; }
|
||||||
size_t allocated_blocks_bytes() const { return _allocated_blocks_words * BytesPerWord; }
|
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_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; }
|
size_t allocated_chunks_count() const { return _allocated_chunks_count; }
|
||||||
|
|
||||||
bool is_humongous(size_t word_size) { return word_size > medium_chunk_size(); }
|
bool is_humongous(size_t word_size) { return word_size > medium_chunk_size(); }
|
||||||
|
@ -3349,6 +3350,16 @@ size_t Metaspace::capacity_bytes_slow(MetadataType mdtype) const {
|
||||||
return capacity_words_slow(mdtype) * BytesPerWord;
|
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) {
|
void Metaspace::deallocate(MetaWord* ptr, size_t word_size, bool is_class) {
|
||||||
assert(!SafepointSynchronize::is_at_safepoint()
|
assert(!SafepointSynchronize::is_at_safepoint()
|
||||||
|| Thread::current()->is_VM_thread(), "should be the VM thread");
|
|| Thread::current()->is_VM_thread(), "should be the VM thread");
|
||||||
|
|
|
@ -225,6 +225,9 @@ class Metaspace : public CHeapObj<mtClass> {
|
||||||
size_t used_bytes_slow(MetadataType mdtype) const;
|
size_t used_bytes_slow(MetadataType mdtype) const;
|
||||||
size_t capacity_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,
|
static MetaWord* allocate(ClassLoaderData* loader_data, size_t word_size,
|
||||||
bool read_only, MetaspaceObj::Type type, TRAPS);
|
bool read_only, MetaspaceObj::Type type, TRAPS);
|
||||||
void deallocate(MetaWord* ptr, size_t byte_size, bool is_class);
|
void deallocate(MetaWord* ptr, size_t byte_size, bool is_class);
|
||||||
|
|
|
@ -98,6 +98,7 @@
|
||||||
template(LinuxDllLoad) \
|
template(LinuxDllLoad) \
|
||||||
template(RotateGCLog) \
|
template(RotateGCLog) \
|
||||||
template(WhiteBoxOperation) \
|
template(WhiteBoxOperation) \
|
||||||
|
template(ClassLoaderStatsOperation) \
|
||||||
|
|
||||||
class VM_Operation: public CHeapObj<mtInternal> {
|
class VM_Operation: public CHeapObj<mtInternal> {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
|
#include "classfile/classLoaderStats.hpp"
|
||||||
#include "gc_implementation/shared/vmGCOperations.hpp"
|
#include "gc_implementation/shared/vmGCOperations.hpp"
|
||||||
#include "runtime/javaCalls.hpp"
|
#include "runtime/javaCalls.hpp"
|
||||||
#include "runtime/os.hpp"
|
#include "runtime/os.hpp"
|
||||||
|
@ -58,6 +59,7 @@ void DCmdRegistrant::register_dcmds(){
|
||||||
#endif // INCLUDE_SERVICES
|
#endif // INCLUDE_SERVICES
|
||||||
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ThreadDumpDCmd>(full_export, true, false));
|
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<RotateGCLogDCmd>(full_export, true, false));
|
||||||
|
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassLoaderStatsDCmd>(full_export, true, false));
|
||||||
|
|
||||||
// Enhanced JMX Agent Support
|
// Enhanced JMX Agent Support
|
||||||
// These commands won't be exported via the DiagnosticCommandMBean until an
|
// These commands won't be exported via the DiagnosticCommandMBean until an
|
||||||
|
|
155
hotspot/test/serviceability/dcmd/ClassLoaderStatsTest.java
Normal file
155
hotspot/test/serviceability/dcmd/ClassLoaderStatsTest.java
Normal file
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue