562 lines
19 KiB
Diff
562 lines
19 KiB
Diff
From 528a3b6459e34532120f468ba9afb8833d516f5a Mon Sep 17 00:00:00 2001
|
|
From: eapen <zhangyipeng7@huawei.com>
|
|
Date: Thu, 15 Dec 2022 11:38:55 +0800
|
|
Subject: [PATCH 19/33] I68TO2: 8189688: NMT: Report per-class load metadata
|
|
information
|
|
---
|
|
hotspot/src/share/vm/memory/metaspace.cpp | 322 ++++++++++++++++++++++++-
|
|
hotspot/src/share/vm/memory/metaspace.hpp | 4 +
|
|
hotspot/src/share/vm/runtime/vm_operations.cpp | 4 +
|
|
hotspot/src/share/vm/runtime/vm_operations.hpp | 12 +
|
|
hotspot/src/share/vm/services/nmtDCmd.cpp | 16 +-
|
|
hotspot/src/share/vm/services/nmtDCmd.hpp | 1 +
|
|
6 files changed, 354 insertions(+), 5 deletions(-)
|
|
|
|
diff --git a/hotspot/src/share/vm/memory/metaspace.cpp b/hotspot/src/share/vm/memory/metaspace.cpp
|
|
index 6c4654b..cf4a112 100644
|
|
--- a/hotspot/src/share/vm/memory/metaspace.cpp
|
|
+++ b/hotspot/src/share/vm/memory/metaspace.cpp
|
|
@@ -75,6 +75,22 @@ enum ChunkIndex {
|
|
NumberOfInUseLists = 4
|
|
};
|
|
|
|
+// Helper, returns a descriptive name for the given index.
|
|
+static const char* chunk_size_name(ChunkIndex index) {
|
|
+ switch (index) {
|
|
+ case SpecializedIndex:
|
|
+ return "specialized";
|
|
+ case SmallIndex:
|
|
+ return "small";
|
|
+ case MediumIndex:
|
|
+ return "medium";
|
|
+ case HumongousIndex:
|
|
+ return "humongous";
|
|
+ default:
|
|
+ return "Invalid index";
|
|
+ }
|
|
+}
|
|
+
|
|
enum ChunkSizes { // in words.
|
|
ClassSpecializedChunk = 128,
|
|
SpecializedChunk = 128,
|
|
@@ -89,6 +105,18 @@ static ChunkIndex next_chunk_index(ChunkIndex i) {
|
|
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;
|
|
@@ -141,6 +169,18 @@ class ChunkManager : public CHeapObj<mtInternal> {
|
|
}
|
|
void verify_free_chunks_count();
|
|
|
|
+ struct ChunkManagerStatistics {
|
|
+ size_t num_by_type[NumberOfFreeLists];
|
|
+ size_t single_size_by_type[NumberOfFreeLists];
|
|
+ size_t total_size_by_type[NumberOfFreeLists];
|
|
+ size_t num_humongous_chunks;
|
|
+ size_t total_size_humongous_chunks;
|
|
+ };
|
|
+
|
|
+ void locked_get_statistics(ChunkManagerStatistics* stat) const;
|
|
+ void get_statistics(ChunkManagerStatistics* stat) const;
|
|
+ static void print_statistics(const ChunkManagerStatistics* stat, outputStream* out, size_t scale);
|
|
+
|
|
public:
|
|
|
|
ChunkManager(size_t specialized_size, size_t small_size, size_t medium_size)
|
|
@@ -157,6 +197,9 @@ class ChunkManager : public CHeapObj<mtInternal> {
|
|
// for special, small, medium, and humongous chunks.
|
|
ChunkIndex list_index(size_t size);
|
|
|
|
+ // Map a given index to the chunk size.
|
|
+ size_t size_by_index(ChunkIndex index) const;
|
|
+
|
|
// Remove the chunk from its freelist. It is
|
|
// expected to be on one of the _free_chunks[] lists.
|
|
void remove_chunk(Metachunk* chunk);
|
|
@@ -249,6 +292,10 @@ class ChunkManager : public CHeapObj<mtInternal> {
|
|
void locked_print_sum_free_chunks(outputStream* st);
|
|
|
|
void print_on(outputStream* st) const;
|
|
+
|
|
+ // Prints composition for both non-class and (if available)
|
|
+ // class chunk manager.
|
|
+ static void print_all_chunkmanagers(outputStream* out, size_t scale = 1);
|
|
};
|
|
|
|
// Used to manage the free list of Metablocks (a block corresponds
|
|
@@ -1707,7 +1754,6 @@ bool Metadebug::test_metadata_failure() {
|
|
#endif
|
|
|
|
// ChunkManager methods
|
|
-
|
|
size_t ChunkManager::free_chunks_total_words() {
|
|
return _free_chunks_total;
|
|
}
|
|
@@ -1729,6 +1775,12 @@ size_t ChunkManager::free_chunks_count() {
|
|
return _free_chunks_count;
|
|
}
|
|
|
|
+size_t ChunkManager::size_by_index(ChunkIndex index) const {
|
|
+ index_bounds_check(index);
|
|
+ assert(index != HumongousIndex, "Do not call for humongous chunks.");
|
|
+ return _free_chunks[index].size();
|
|
+}
|
|
+
|
|
void ChunkManager::locked_verify_free_chunks_total() {
|
|
assert_lock_strong(SpaceManager::expand_lock());
|
|
assert(sum_free_chunks() == _free_chunks_total,
|
|
@@ -1918,7 +1970,83 @@ Metachunk* ChunkManager::chunk_freelist_allocate(size_t word_size) {
|
|
|
|
void ChunkManager::print_on(outputStream* out) const {
|
|
if (PrintFLSStatistics != 0) {
|
|
- const_cast<ChunkManager *>(this)->humongous_dictionary()->report_statistics();
|
|
+ _humongous_dictionary.report_statistics();
|
|
+ }
|
|
+}
|
|
+
|
|
+void ChunkManager::locked_get_statistics(ChunkManagerStatistics* stat) const {
|
|
+ assert_lock_strong(SpaceManager::expand_lock());
|
|
+ for (ChunkIndex i = ZeroIndex; i < NumberOfFreeLists; i = next_chunk_index(i)) {
|
|
+ stat->num_by_type[i] = num_free_chunks(i);
|
|
+ stat->single_size_by_type[i] = size_by_index(i);
|
|
+ stat->total_size_by_type[i] = size_free_chunks_in_bytes(i);
|
|
+ }
|
|
+ stat->num_humongous_chunks = num_free_chunks(HumongousIndex);
|
|
+ stat->total_size_humongous_chunks = size_free_chunks_in_bytes(HumongousIndex);
|
|
+}
|
|
+
|
|
+void ChunkManager::get_statistics(ChunkManagerStatistics* stat) const {
|
|
+ MutexLockerEx cl(SpaceManager::expand_lock(),
|
|
+ Mutex::_no_safepoint_check_flag);
|
|
+ locked_get_statistics(stat);
|
|
+}
|
|
+
|
|
+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(" " 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];
|
|
+ }
|
|
+
|
|
+ total += stat->total_size_humongous_chunks;
|
|
+
|
|
+ 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, 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;
|
|
+ out->print_cr("Chunkmanager (non-class):");
|
|
+ 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, scale);
|
|
+ } else {
|
|
+ out->print_cr("unavailable.");
|
|
+ }
|
|
+ out->print_cr("Chunkmanager (class):");
|
|
+ const ChunkManager* const class_cm = Metaspace::chunk_manager_class();
|
|
+ if (class_cm != NULL) {
|
|
+ class_cm->get_statistics(&stat);
|
|
+ ChunkManager::print_statistics(&stat, out, scale);
|
|
+ } else {
|
|
+ out->print_cr("unavailable.");
|
|
}
|
|
}
|
|
|
|
@@ -2930,6 +3058,195 @@ void MetaspaceAux::print_waste(outputStream* out) {
|
|
}
|
|
}
|
|
|
|
+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:");
|
|
@@ -3743,6 +4060,7 @@ void Metaspace::report_metadata_oome(ClassLoaderData* loader_data, size_t word_s
|
|
loader_data->dump(gclog_or_tty);
|
|
}
|
|
MetaspaceAux::dump(gclog_or_tty);
|
|
+ ChunkManager::print_all_chunkmanagers(gclog_or_tty);
|
|
}
|
|
|
|
bool out_of_compressed_class_space = false;
|
|
diff --git a/hotspot/src/share/vm/memory/metaspace.hpp b/hotspot/src/share/vm/memory/metaspace.hpp
|
|
index 122dd4b..ff1b232 100644
|
|
--- a/hotspot/src/share/vm/memory/metaspace.hpp
|
|
+++ b/hotspot/src/share/vm/memory/metaspace.hpp
|
|
@@ -65,6 +65,7 @@ class MetaspaceTracer;
|
|
class MetaWord;
|
|
class Mutex;
|
|
class outputStream;
|
|
+class PrintCLDMetaspaceInfoClosure;
|
|
class SpaceManager;
|
|
class VirtualSpaceList;
|
|
|
|
@@ -88,6 +89,7 @@ class Metaspace : public CHeapObj<mtClass> {
|
|
friend class VM_CollectForMetadataAllocation;
|
|
friend class MetaspaceGC;
|
|
friend class MetaspaceAux;
|
|
+ friend class PrintCLDMetaspaceInfoClosure;
|
|
|
|
public:
|
|
enum MetadataType {
|
|
@@ -372,6 +374,8 @@ class MetaspaceAux : AllStatic {
|
|
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);
|
|
|
|
diff --git a/hotspot/src/share/vm/runtime/vm_operations.cpp b/hotspot/src/share/vm/runtime/vm_operations.cpp
|
|
index d401ea6..b42d18f 100644
|
|
--- a/hotspot/src/share/vm/runtime/vm_operations.cpp
|
|
+++ b/hotspot/src/share/vm/runtime/vm_operations.cpp
|
|
@@ -209,6 +209,10 @@ void VM_PrintJNI::doit() {
|
|
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;
|
|
diff --git a/hotspot/src/share/vm/runtime/vm_operations.hpp b/hotspot/src/share/vm/runtime/vm_operations.hpp
|
|
index 3744040..19c33f8 100644
|
|
--- a/hotspot/src/share/vm/runtime/vm_operations.hpp
|
|
+++ b/hotspot/src/share/vm/runtime/vm_operations.hpp
|
|
@@ -101,6 +101,7 @@
|
|
template(ClassLoaderHierarchyOperation) \
|
|
template(JFROldObject) \
|
|
template(PrintClasses) \
|
|
+ template(PrintMetadata) \
|
|
|
|
class VM_Operation: public CHeapObj<mtInternal> {
|
|
public:
|
|
@@ -329,6 +330,17 @@ class VM_PrintJNI: public VM_Operation {
|
|
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:
|
|
diff --git a/hotspot/src/share/vm/services/nmtDCmd.cpp b/hotspot/src/share/vm/services/nmtDCmd.cpp
|
|
index fcad784..659ca33 100644
|
|
--- a/hotspot/src/share/vm/services/nmtDCmd.cpp
|
|
+++ b/hotspot/src/share/vm/services/nmtDCmd.cpp
|
|
@@ -24,6 +24,8 @@
|
|
#include "precompiled.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 @@ NMTDCmd::NMTDCmd(outputStream* output,
|
|
_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 @@ NMTDCmd::NMTDCmd(outputStream* output,
|
|
"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 @@ void NMTDCmd::execute(DCmdSource source, TRAPS) {
|
|
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 @@ void NMTDCmd::execute(DCmdSource source, TRAPS) {
|
|
|
|
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 @@ void NMTDCmd::execute(DCmdSource source, TRAPS) {
|
|
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)) {
|
|
diff --git a/hotspot/src/share/vm/services/nmtDCmd.hpp b/hotspot/src/share/vm/services/nmtDCmd.hpp
|
|
index df1ab36..bbd1391 100644
|
|
--- a/hotspot/src/share/vm/services/nmtDCmd.hpp
|
|
+++ b/hotspot/src/share/vm/services/nmtDCmd.hpp
|
|
@@ -39,6 +39,7 @@ class NMTDCmd: public DCmdWithParser {
|
|
protected:
|
|
DCmdArgument<bool> _summary;
|
|
DCmdArgument<bool> _detail;
|
|
+ DCmdArgument<bool> _metadata;
|
|
DCmdArgument<bool> _baseline;
|
|
DCmdArgument<bool> _summary_diff;
|
|
DCmdArgument<bool> _detail_diff;
|
|
--
|
|
1.8.3.1
|