8189688: NMT: Report per-class load metadata information
Summary: Report per-class loader metadata info via NMT jcmd metadata sub-command
Reviewed-by: stuefe, coleenp
--- a/src/hotspot/share/memory/metaspace.cpp Tue Nov 07 10:30:53 2017 -0500
+++ b/src/hotspot/share/memory/metaspace.cpp Tue Nov 07 09:37:45 2017 -0500
@@ -108,6 +108,18 @@
return (ChunkIndex) (i+1);
}
+static const char* scale_unit(size_t scale) {
+ switch(scale) {
+ case 1: return "BYTES";
+ case K: return "KB";
+ case M: return "MB";
+ case G: return "GB";
+ default:
+ ShouldNotReachHere();
+ return NULL;
+ }
+}
+
volatile intptr_t MetaspaceGC::_capacity_until_GC = 0;
uint MetaspaceGC::_shrink_factor = 0;
bool MetaspaceGC::_should_concurrent_collect = false;
@@ -176,7 +188,7 @@
void locked_get_statistics(ChunkManagerStatistics* stat) const;
void get_statistics(ChunkManagerStatistics* stat) const;
- static void print_statistics(const ChunkManagerStatistics* stat, outputStream* out);
+ static void print_statistics(const ChunkManagerStatistics* stat, outputStream* out, size_t scale);
public:
@@ -283,7 +295,7 @@
// Prints composition for both non-class and (if available)
// class chunk manager.
- static void print_all_chunkmanagers(outputStream* out);
+ static void print_all_chunkmanagers(outputStream* out, size_t scale = 1);
};
class SmallBlocks : public CHeapObj<mtClass> {
@@ -1724,7 +1736,6 @@
#endif
// ChunkManager methods
-
size_t ChunkManager::free_chunks_total_words() {
return _free_chunks_total;
}
@@ -2057,22 +2068,45 @@
locked_get_statistics(stat);
}
-void ChunkManager::print_statistics(const ChunkManagerStatistics* stat, outputStream* out) {
+void ChunkManager::print_statistics(const ChunkManagerStatistics* stat, outputStream* out, size_t scale) {
size_t total = 0;
+ assert(scale == 1 || scale == K || scale == M || scale == G, "Invalid scale");
+
+ const char* unit = scale_unit(scale);
for (ChunkIndex i = ZeroIndex; i < NumberOfFreeLists; i = next_chunk_index(i)) {
- out->print_cr(" " SIZE_FORMAT " %s (" SIZE_FORMAT " bytes) chunks, total " SIZE_FORMAT " bytes",
- stat->num_by_type[i], chunk_size_name(i),
- stat->single_size_by_type[i],
- stat->total_size_by_type[i]);
+ out->print(" " SIZE_FORMAT " %s (" SIZE_FORMAT " bytes) chunks, total ",
+ stat->num_by_type[i], chunk_size_name(i),
+ stat->single_size_by_type[i]);
+ if (scale == 1) {
+ out->print_cr(SIZE_FORMAT " bytes", stat->total_size_by_type[i]);
+ } else {
+ out->print_cr("%.2f%s", (float)stat->total_size_by_type[i] / scale, unit);
+ }
+
total += stat->total_size_by_type[i];
}
- out->print_cr(" " SIZE_FORMAT " humongous chunks, total " SIZE_FORMAT " bytes",
- stat->num_humongous_chunks, stat->total_size_humongous_chunks);
+
+
total += stat->total_size_humongous_chunks;
- out->print_cr(" total size: " SIZE_FORMAT ".", total);
+
+ if (scale == 1) {
+ out->print_cr(" " SIZE_FORMAT " humongous chunks, total " SIZE_FORMAT " bytes",
+ stat->num_humongous_chunks, stat->total_size_humongous_chunks);
+
+ out->print_cr(" total size: " SIZE_FORMAT " bytes.", total);
+ } else {
+ out->print_cr(" " SIZE_FORMAT " humongous chunks, total %.2f%s",
+ stat->num_humongous_chunks,
+ (float)stat->total_size_humongous_chunks / scale, unit);
+
+ out->print_cr(" total size: %.2f%s.", (float)total / scale, unit);
+ }
+
}
-void ChunkManager::print_all_chunkmanagers(outputStream* out) {
+void ChunkManager::print_all_chunkmanagers(outputStream* out, size_t scale) {
+ assert(scale == 1 || scale == K || scale == M || scale == G, "Invalid scale");
+
// Note: keep lock protection only to retrieving statistics; keep printing
// out of lock protection
ChunkManagerStatistics stat;
@@ -2080,7 +2114,7 @@
const ChunkManager* const non_class_cm = Metaspace::chunk_manager_metadata();
if (non_class_cm != NULL) {
non_class_cm->get_statistics(&stat);
- ChunkManager::print_statistics(&stat, out);
+ ChunkManager::print_statistics(&stat, out, scale);
} else {
out->print_cr("unavailable.");
}
@@ -2088,7 +2122,7 @@
const ChunkManager* const class_cm = Metaspace::chunk_manager_class();
if (class_cm != NULL) {
class_cm->get_statistics(&stat);
- ChunkManager::print_statistics(&stat, out);
+ ChunkManager::print_statistics(&stat, out, scale);
} else {
out->print_cr("unavailable.");
}
@@ -2981,6 +3015,194 @@
}
}
+class MetadataStats VALUE_OBJ_CLASS_SPEC {
+private:
+ size_t _capacity;
+ size_t _used;
+ size_t _free;
+ size_t _waste;
+
+public:
+ MetadataStats() : _capacity(0), _used(0), _free(0), _waste(0) { }
+ MetadataStats(size_t capacity, size_t used, size_t free, size_t waste)
+ : _capacity(capacity), _used(used), _free(free), _waste(waste) { }
+
+ void add(const MetadataStats& stats) {
+ _capacity += stats.capacity();
+ _used += stats.used();
+ _free += stats.free();
+ _waste += stats.waste();
+ }
+
+ size_t capacity() const { return _capacity; }
+ size_t used() const { return _used; }
+ size_t free() const { return _free; }
+ size_t waste() const { return _waste; }
+
+ void print_on(outputStream* out, size_t scale) const;
+};
+
+
+void MetadataStats::print_on(outputStream* out, size_t scale) const {
+ const char* unit = scale_unit(scale);
+ out->print_cr("capacity=%10.2f%s used=%10.2f%s free=%10.2f%s waste=%10.2f%s",
+ (float)capacity() / scale, unit,
+ (float)used() / scale, unit,
+ (float)free() / scale, unit,
+ (float)waste() / scale, unit);
+}
+
+class PrintCLDMetaspaceInfoClosure : public CLDClosure {
+private:
+ outputStream* _out;
+ size_t _scale;
+
+ size_t _total_count;
+ MetadataStats _total_metadata;
+ MetadataStats _total_class;
+
+ size_t _total_anon_count;
+ MetadataStats _total_anon_metadata;
+ MetadataStats _total_anon_class;
+
+public:
+ PrintCLDMetaspaceInfoClosure(outputStream* out, size_t scale = K)
+ : _out(out), _scale(scale), _total_count(0), _total_anon_count(0) { }
+
+ ~PrintCLDMetaspaceInfoClosure() {
+ print_summary();
+ }
+
+ void do_cld(ClassLoaderData* cld) {
+ assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint");
+
+ if (cld->is_unloading()) return;
+ Metaspace* msp = cld->metaspace_or_null();
+ if (msp == NULL) {
+ return;
+ }
+
+ bool anonymous = false;
+ if (cld->is_anonymous()) {
+ _out->print_cr("ClassLoader: for anonymous class");
+ anonymous = true;
+ } else {
+ ResourceMark rm;
+ _out->print_cr("ClassLoader: %s", cld->loader_name());
+ }
+
+ print_metaspace(msp, anonymous);
+ _out->cr();
+ }
+
+private:
+ void print_metaspace(Metaspace* msp, bool anonymous);
+ void print_summary() const;
+};
+
+void PrintCLDMetaspaceInfoClosure::print_metaspace(Metaspace* msp, bool anonymous){
+ assert(msp != NULL, "Sanity");
+ SpaceManager* vsm = msp->vsm();
+ const char* unit = scale_unit(_scale);
+
+ size_t capacity = vsm->sum_capacity_in_chunks_in_use() * BytesPerWord;
+ size_t used = vsm->sum_used_in_chunks_in_use() * BytesPerWord;
+ size_t free = vsm->sum_free_in_chunks_in_use() * BytesPerWord;
+ size_t waste = vsm->sum_waste_in_chunks_in_use() * BytesPerWord;
+
+ _total_count ++;
+ MetadataStats metadata_stats(capacity, used, free, waste);
+ _total_metadata.add(metadata_stats);
+
+ if (anonymous) {
+ _total_anon_count ++;
+ _total_anon_metadata.add(metadata_stats);
+ }
+
+ _out->print(" Metadata ");
+ metadata_stats.print_on(_out, _scale);
+
+ if (Metaspace::using_class_space()) {
+ vsm = msp->class_vsm();
+
+ capacity = vsm->sum_capacity_in_chunks_in_use() * BytesPerWord;
+ used = vsm->sum_used_in_chunks_in_use() * BytesPerWord;
+ free = vsm->sum_free_in_chunks_in_use() * BytesPerWord;
+ waste = vsm->sum_waste_in_chunks_in_use() * BytesPerWord;
+
+ MetadataStats class_stats(capacity, used, free, waste);
+ _total_class.add(class_stats);
+
+ if (anonymous) {
+ _total_anon_class.add(class_stats);
+ }
+
+ _out->print(" Class data ");
+ class_stats.print_on(_out, _scale);
+ }
+}
+
+void PrintCLDMetaspaceInfoClosure::print_summary() const {
+ const char* unit = scale_unit(_scale);
+ _out->cr();
+ _out->print_cr("Summary:");
+
+ MetadataStats total;
+ total.add(_total_metadata);
+ total.add(_total_class);
+
+ _out->print(" Total class loaders=" SIZE_FORMAT_W(6) " ", _total_count);
+ total.print_on(_out, _scale);
+
+ _out->print(" Metadata ");
+ _total_metadata.print_on(_out, _scale);
+
+ if (Metaspace::using_class_space()) {
+ _out->print(" Class data ");
+ _total_class.print_on(_out, _scale);
+ }
+ _out->cr();
+
+ MetadataStats total_anon;
+ total_anon.add(_total_anon_metadata);
+ total_anon.add(_total_anon_class);
+
+ _out->print("For anonymous classes=" SIZE_FORMAT_W(6) " ", _total_anon_count);
+ total_anon.print_on(_out, _scale);
+
+ _out->print(" Metadata ");
+ _total_anon_metadata.print_on(_out, _scale);
+
+ if (Metaspace::using_class_space()) {
+ _out->print(" Class data ");
+ _total_anon_class.print_on(_out, _scale);
+ }
+}
+
+void MetaspaceAux::print_metadata_for_nmt(outputStream* out, size_t scale) {
+ const char* unit = scale_unit(scale);
+ out->print_cr("Metaspaces:");
+ out->print_cr(" Metadata space: reserved=" SIZE_FORMAT_W(10) "%s committed=" SIZE_FORMAT_W(10) "%s",
+ reserved_bytes(Metaspace::NonClassType) / scale, unit,
+ committed_bytes(Metaspace::NonClassType) / scale, unit);
+ if (Metaspace::using_class_space()) {
+ out->print_cr(" Class space: reserved=" SIZE_FORMAT_W(10) "%s committed=" SIZE_FORMAT_W(10) "%s",
+ reserved_bytes(Metaspace::ClassType) / scale, unit,
+ committed_bytes(Metaspace::ClassType) / scale, unit);
+ }
+
+ out->cr();
+ ChunkManager::print_all_chunkmanagers(out, scale);
+
+ out->cr();
+ out->print_cr("Per-classloader metadata:");
+ out->cr();
+
+ PrintCLDMetaspaceInfoClosure cl(out, scale);
+ ClassLoaderDataGraph::cld_do(&cl);
+}
+
+
// Dump global metaspace things from the end of ClassLoaderDataGraph
void MetaspaceAux::dump(outputStream* out) {
out->print_cr("All Metaspace:");
--- a/src/hotspot/share/memory/metaspace.hpp Tue Nov 07 10:30:53 2017 -0500
+++ b/src/hotspot/share/memory/metaspace.hpp Tue Nov 07 09:37:45 2017 -0500
@@ -63,6 +63,7 @@
class MetaWord;
class Mutex;
class outputStream;
+class PrintCLDMetaspaceInfoClosure;
class SpaceManager;
class VirtualSpaceList;
@@ -87,6 +88,7 @@
friend class MetaspaceAux;
friend class MetaspaceShared;
friend class CollectorPolicy;
+ friend class PrintCLDMetaspaceInfoClosure;
public:
enum MetadataType {
@@ -347,6 +349,8 @@
return min_chunk_size_words() * BytesPerWord;
}
+ static void print_metadata_for_nmt(outputStream* out, size_t scale = K);
+
static bool has_chunk_free_list(Metaspace::MetadataType mdtype);
static MetaspaceChunkFreeListSummary chunk_free_list_summary(Metaspace::MetadataType mdtype);
--- a/src/hotspot/share/runtime/vm_operations.cpp Tue Nov 07 10:30:53 2017 -0500
+++ b/src/hotspot/share/runtime/vm_operations.cpp Tue Nov 07 09:37:45 2017 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, 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
@@ -230,6 +230,10 @@
JNIHandles::print_on(_out);
}
+void VM_PrintMetadata::doit() {
+ MetaspaceAux::print_metadata_for_nmt(_out, _scale);
+}
+
VM_FindDeadlocks::~VM_FindDeadlocks() {
if (_deadlocks != NULL) {
DeadlockCycle* cycle = _deadlocks;
--- a/src/hotspot/share/runtime/vm_operations.hpp Tue Nov 07 10:30:53 2017 -0500
+++ b/src/hotspot/share/runtime/vm_operations.hpp Tue Nov 07 09:37:45 2017 -0500
@@ -111,6 +111,7 @@
template(ThreadsSuspendJVMTI) \
template(ICBufferFull) \
template(ScavengeMonitors) \
+ template(PrintMetadata) \
class VM_Operation: public CHeapObj<mtInternal> {
public:
@@ -374,6 +375,17 @@
void doit();
};
+class VM_PrintMetadata : public VM_Operation {
+ private:
+ outputStream* _out;
+ size_t _scale;
+ public:
+ VM_PrintMetadata(outputStream* out, size_t scale) : _out(out), _scale(scale) {};
+
+ VMOp_Type type() const { return VMOp_PrintMetadata; }
+ void doit();
+};
+
class DeadlockCycle;
class VM_FindDeadlocks: public VM_Operation {
private:
--- a/src/hotspot/share/services/nmtDCmd.cpp Tue Nov 07 10:30:53 2017 -0500
+++ b/src/hotspot/share/services/nmtDCmd.cpp Tue Nov 07 09:37:45 2017 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, 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
@@ -24,6 +24,8 @@
#include "precompiled.hpp"
#include "memory/resourceArea.hpp"
#include "runtime/mutexLocker.hpp"
+#include "runtime/vmThread.hpp"
+#include "runtime/vm_operations.hpp"
#include "services/nmtDCmd.hpp"
#include "services/memReporter.hpp"
#include "services/memTracker.hpp"
@@ -38,6 +40,8 @@
_detail("detail", "request runtime to report memory allocation >= "
"1K by each callsite.",
"BOOLEAN", false, "false"),
+ _metadata("metadata", "request runtime to report metadata information",
+ "BOOLEAN", false, "false"),
_baseline("baseline", "request runtime to baseline current memory usage, " \
"so it can be compared against in later time.",
"BOOLEAN", false, "false"),
@@ -57,6 +61,7 @@
"STRING", false, "KB") {
_dcmdparser.add_dcmd_option(&_summary);
_dcmdparser.add_dcmd_option(&_detail);
+ _dcmdparser.add_dcmd_option(&_metadata);
_dcmdparser.add_dcmd_option(&_baseline);
_dcmdparser.add_dcmd_option(&_summary_diff);
_dcmdparser.add_dcmd_option(&_detail_diff);
@@ -92,6 +97,7 @@
int nopt = 0;
if (_summary.is_set() && _summary.value()) { ++nopt; }
if (_detail.is_set() && _detail.value()) { ++nopt; }
+ if (_metadata.is_set() && _metadata.value()) { ++nopt; }
if (_baseline.is_set() && _baseline.value()) { ++nopt; }
if (_summary_diff.is_set() && _summary_diff.value()) { ++nopt; }
if (_detail_diff.is_set() && _detail_diff.value()) { ++nopt; }
@@ -100,7 +106,7 @@
if (nopt > 1) {
output()->print_cr("At most one of the following option can be specified: " \
- "summary, detail, baseline, summary.diff, detail.diff, shutdown");
+ "summary, detail, metadata, baseline, summary.diff, detail.diff, shutdown");
return;
} else if (nopt == 0) {
if (_summary.is_set()) {
@@ -118,9 +124,13 @@
report(true, scale_unit);
} else if (_detail.value()) {
if (!check_detail_tracking_level(output())) {
- return;
- }
+ return;
+ }
report(false, scale_unit);
+ } else if (_metadata.value()) {
+ size_t scale = get_scale(_scale.value());
+ VM_PrintMetadata op(output(), scale);
+ VMThread::execute(&op);
} else if (_baseline.value()) {
MemBaseline& baseline = MemTracker::get_baseline();
if (!baseline.baseline(MemTracker::tracking_level() != NMT_detail)) {
--- a/src/hotspot/share/services/nmtDCmd.hpp Tue Nov 07 10:30:53 2017 -0500
+++ b/src/hotspot/share/services/nmtDCmd.hpp Tue Nov 07 09:37:45 2017 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, 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
@@ -39,6 +39,7 @@
protected:
DCmdArgument<bool> _summary;
DCmdArgument<bool> _detail;
+ DCmdArgument<bool> _metadata;
DCmdArgument<bool> _baseline;
DCmdArgument<bool> _summary_diff;
DCmdArgument<bool> _detail_diff;