# HG changeset patch # User zgu # Date 1510065465 18000 # Node ID 18dccdc438d7859047ac463fd4777456aec1297d # Parent c7b50c23ea7164633005c1d5e17ed22c3fc300f6 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 diff -r c7b50c23ea71 -r 18dccdc438d7 src/hotspot/share/memory/metaspace.cpp --- 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 { @@ -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:"); diff -r c7b50c23ea71 -r 18dccdc438d7 src/hotspot/share/memory/metaspace.hpp --- 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); diff -r c7b50c23ea71 -r 18dccdc438d7 src/hotspot/share/runtime/vm_operations.cpp --- 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; diff -r c7b50c23ea71 -r 18dccdc438d7 src/hotspot/share/runtime/vm_operations.hpp --- 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 { 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: diff -r c7b50c23ea71 -r 18dccdc438d7 src/hotspot/share/services/nmtDCmd.cpp --- 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)) { diff -r c7b50c23ea71 -r 18dccdc438d7 src/hotspot/share/services/nmtDCmd.hpp --- 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 _summary; DCmdArgument _detail; + DCmdArgument _metadata; DCmdArgument _baseline; DCmdArgument _summary_diff; DCmdArgument _detail_diff;