# HG changeset patch # User stuefe # Date 1524585992 -7200 # Tue Apr 24 18:06:32 2018 +0200 # Node ID 2a8f7cb7da9e0520424ebce193ec0e5fe6dd920e # Parent c830e94b56062cb8fa4c1db3f95789b94abc2e92 [mq]: 8201572-improve-metaspace-reporting diff --git a/src/hotspot/share/memory/metachunk.hpp b/src/hotspot/share/memory/metachunk.hpp --- a/src/hotspot/share/memory/metachunk.hpp +++ b/src/hotspot/share/memory/metachunk.hpp @@ -110,6 +110,9 @@ size_t get_size_for_nonhumongous_chunktype(ChunkIndex chunk_type, bool is_class); ChunkIndex get_chunk_type_by_size(size_t size, bool is_class); +ChunkIndex next_chunk_index(ChunkIndex i); +ChunkIndex prev_chunk_index(ChunkIndex i); + // Returns a descriptive name for a chunk type. const char* chunk_size_name(ChunkIndex index); @@ -184,7 +187,7 @@ // Alignment of each allocation in the chunks. static size_t object_alignment(); - // Size of the Metachunk header, including alignment. + // Size of the Metachunk header, in words, including alignment. static size_t overhead(); Metachunk(ChunkIndex chunktype, bool is_class, size_t word_size, VirtualSpaceNode* container); diff --git a/src/hotspot/share/memory/metaspace.cpp b/src/hotspot/share/memory/metaspace.cpp --- a/src/hotspot/share/memory/metaspace.cpp +++ b/src/hotspot/share/memory/metaspace.cpp @@ -33,6 +33,8 @@ #include "memory/freeList.inline.hpp" #include "memory/metachunk.hpp" #include "memory/metaspace.hpp" +#include "memory/metaspace/metaspaceCommon.hpp" +#include "memory/metaspace/metaspaceStatistics.hpp" #include "memory/metaspaceGCThresholdUpdater.hpp" #include "memory/metaspaceShared.hpp" #include "memory/metaspaceTracer.hpp" @@ -43,6 +45,7 @@ #include "runtime/init.hpp" #include "runtime/java.hpp" #include "runtime/mutex.hpp" +#include "runtime/mutexLocker.hpp" #include "runtime/orderAccess.inline.hpp" #include "services/memTracker.hpp" #include "services/memoryService.hpp" @@ -51,6 +54,8 @@ #include "utilities/debug.hpp" #include "utilities/macros.hpp" +using namespace metaspace::internals; + typedef BinaryTreeDictionary > BlockTreeDictionary; typedef BinaryTreeDictionary > ChunkTreeDictionary; @@ -70,6 +75,30 @@ DEBUG_ONLY(bool Metaspace::_frozen = false;) +// Internal statistics. +#ifdef ASSERT +static struct { + // Number of allocations. + uintx num_allocs; + // Number of times a ClassLoaderMetaspace was born... + uintx num_metaspace_births; + // ... and died. + uintx num_metaspace_deaths; + // Number of times VirtualSpaceListNodes were created... + uintx num_vsnodes_created; + // ... and purged. + uintx num_vsnodes_purged; + // Number of times we expanded the committed section of the space. + uintx num_committed_space_expanded; + // Number of deallocations + uintx num_deallocs; + // Number of deallocations triggered from outside ("real" deallocations). + uintx num_external_deallocs; + // Number of times an allocation was satisfied from deallocated blocks. + uintx num_allocs_from_deallocated_blocks; +} g_internal_statistics; +#endif + enum ChunkSizes { // in words. ClassSpecializedChunk = 128, SpecializedChunk = 128, @@ -133,33 +162,34 @@ return (ChunkIndex)-1; } - -static ChunkIndex next_chunk_index(ChunkIndex i) { +ChunkIndex next_chunk_index(ChunkIndex i) { assert(i < NumberOfInUseLists, "Out of bound"); return (ChunkIndex) (i+1); } -static ChunkIndex prev_chunk_index(ChunkIndex i) { +ChunkIndex prev_chunk_index(ChunkIndex i) { assert(i > ZeroIndex, "Out of bound"); 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; - } +static const char* space_type_name(Metaspace::MetaspaceType t) { + const char* s = NULL; + switch (t) { + case Metaspace::StandardMetaspaceType: s = "Standard"; break; + case Metaspace::BootMetaspaceType: s = "Boot"; break; + case Metaspace::AnonymousMetaspaceType: s = "Anonymous"; break; + case Metaspace::ReflectionMetaspaceType: s = "Reflection"; break; + default: ShouldNotReachHere(); + } + assert(s != NULL, "Invalid space type"); + return s; } volatile intptr_t MetaspaceGC::_capacity_until_GC = 0; uint MetaspaceGC::_shrink_factor = 0; bool MetaspaceGC::_should_concurrent_collect = false; + typedef class FreeList ChunkList; // Manages the global free lists of chunks. @@ -240,19 +270,6 @@ public: - 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); - - ChunkManager(bool is_class) : _is_class(is_class), _free_chunks_total(0), _free_chunks_count(0) { _free_chunks[SpecializedIndex].set_size(get_size_for_nonhumongous_chunktype(SpecializedIndex, is_class)); @@ -358,9 +375,9 @@ 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); + // Fill in current statistic values to the given statistics object. + void collect_statistics(ChunkManagerStatistics* out) const; + }; class SmallBlocks : public CHeapObj { @@ -383,6 +400,7 @@ } } + // Returns the total size, in words, of all blocks, across all block sizes. size_t total_size() const { size_t result = 0; for (uint i = _small_block_min_size; i < _small_block_max_size; i++) { @@ -392,6 +410,16 @@ return result; } + // Returns the total number of all blocks across all block sizes. + uintx total_num_blocks() const { + uintx result = 0; + for (uint i = _small_block_min_size; i < _small_block_max_size; i++) { + uint k = i - _small_block_min_size; + result = result + _small_lists[k].count(); + } + return result; + } + static uint small_block_max_size() { return _small_block_max_size; } static uint small_block_min_size() { return _small_block_min_size; } @@ -444,6 +472,7 @@ MetaWord* get_block(size_t word_size); void return_block(MetaWord* p, size_t word_size); + // Returns the total size, in words, of all blocks kept in this structure. size_t total_size() const { size_t result = dictionary()->total_size(); if (_small_blocks != NULL) { @@ -452,6 +481,15 @@ return result; } + // Returns the number of all blocks kept in this structure. + uintx num_blocks() const { + uintx result = dictionary()->total_free_blocks(); + if (_small_blocks != NULL) { + result = result + _small_blocks->total_num_blocks(); + } + return result; + } + static size_t min_dictionary_size() { return TreeChunk >::min_size(); } void print_on(outputStream* st) const; }; @@ -857,7 +895,8 @@ void retire(ChunkManager* chunk_manager); - void print_on(outputStream* st) const; + void print_on(outputStream* st) const { print_on(st, K); } + void print_on(outputStream* st, size_t scale) const; void print_map(outputStream* st, bool is_class) const; // Debug support @@ -875,6 +914,12 @@ SIZE_FORMAT_HEX " is not aligned to " \ SIZE_FORMAT, (size_t)(uintptr_t)value, (alignment)) +#define assert_counter(expected_value, real_value, msg) \ + assert( (expected_value) == (real_value), \ + "Counter mismatch (%s): expected " SIZE_FORMAT \ + ", but got: " SIZE_FORMAT ".", msg, expected_value, \ + real_value); + // Decide if large pages should be committed when the memory is reserved. static bool should_commit_large_pages_when_reserving(size_t bytes) { if (UseLargePages && UseLargePagesInMetaspace && !os::can_commit_large_page_memory()) { @@ -1181,7 +1226,8 @@ // Unlink empty VirtualSpaceNodes and free it. void purge(ChunkManager* chunk_manager); - void print_on(outputStream* st) const; + void print_on(outputStream* st) const { print_on(st, K); } + void print_on(outputStream* st, size_t scale) const; void print_map(outputStream* st) const; class VirtualSpaceListIterator : public StackObj { @@ -1218,6 +1264,7 @@ int Metadebug::_allocation_fail_alot_count = 0; + // SpaceManager - used by Metaspace to handle allocations class SpaceManager : public CHeapObj { friend class ClassLoaderMetaspace; @@ -1247,12 +1294,13 @@ // metadata space to a SpaceManager static uint const _anon_and_delegating_metadata_specialize_chunk_limit; - // Sum of all space in allocated chunks - size_t _allocated_blocks_words; - - // Sum of all allocated chunks - size_t _allocated_chunks_words; - size_t _allocated_chunks_count; + // Some running counters, but lets keep their number small to not add to much to + // the per-classloader footprint. + // Note: capacity = used + free + waste + overhead. We do not keep running counters for + // free and waste. Their sum can be deduced from the three other values. + size_t _overhead_words; + size_t _capacity_words; + size_t _used_words; // Free lists of blocks are per SpaceManager since they // are assumed to be in chunks in use by the SpaceManager @@ -1287,6 +1335,12 @@ Mutex* lock() const { return _lock; } + // Adds to the given statistic object. Expects to be locked with lock(). + void add_to_statistics_locked(SpaceManagerStatistics* out) const; + + // Verify internal counters against the current state. Expects to be locked with lock(). + DEBUG_ONLY(void verify_metrics_locked() const;) + protected: void initialize(); @@ -1317,25 +1371,21 @@ size_t medium_chunk_bunch() const { return medium_chunk_size() * MediumChunkMultiple; } - 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(); } - // Increment the per Metaspace and global running sums for Metachunks - // by the given size. This is used when a Metachunk to added to - // the in-use list. - void inc_size_metrics(size_t words); - // Increment the per Metaspace and global running sums Metablocks by the given - // size. This is used when a Metablock is allocated. - void inc_used_metrics(size_t words); - // Delete the portion of the running sums for this SpaceManager. That is, - // the globals running sums for the Metachunks and Metablocks are - // decremented for all the Metachunks in-use by this SpaceManager. - void dec_total_from_size_metrics(); + size_t capacity_words() const { return _capacity_words; } + size_t used_words() const { return _used_words; } + size_t overhead_words() const { return _overhead_words; } + + // Adjust local, global counters after a new chunk has been added. + void account_for_new_chunk(const Metachunk* new_chunk); + + // Adjust local, global counters after space has been allocated from the current chunk. + void account_for_allocation(size_t words); + + // Adjust global counters just before the SpaceManager dies, after all its chunks + // have been returned to the freelist. + void account_for_spacemanager_death(); // Adjust the initial chunk size to match one of the fixed chunk list sizes, // or return the unadjusted size if the requested size is humongous. @@ -1345,13 +1395,7 @@ // Get the initial chunks size for this metaspace type. size_t get_initial_chunk_size(Metaspace::MetaspaceType type) const; - size_t sum_capacity_in_chunks_in_use() const; - size_t sum_used_in_chunks_in_use() const; - size_t sum_free_in_chunks_in_use() const; - size_t sum_waste_in_chunks_in_use() const; - size_t sum_waste_in_chunks_in_use(ChunkIndex index ) const; - - size_t sum_count_in_chunks_in_use(); + // Todo: remove this once we have counters by chunk type. size_t sum_count_in_chunks_in_use(ChunkIndex i); Metachunk* get_new_chunk(size_t chunk_word_size); @@ -1380,15 +1424,11 @@ // debugging support. - void dump(outputStream* const out) const; void print_on(outputStream* st) const; void locked_print_chunks_in_use_on(outputStream* st) const; void verify(); void verify_chunk_size(Metachunk* chunk); -#ifdef ASSERT - void verify_allocated_blocks_words(); -#endif // This adjusts the size given to be greater than the minimum allocation size in // words for data in metaspace. Esentially the minimum size is currently 3 words. @@ -1403,6 +1443,13 @@ return raw_word_size; } + + // Adds to the given statistic object. + void add_to_statistics(SpaceManagerStatistics* out) const; + + // Verify internal counters against the current state. + DEBUG_ONLY(void verify_metrics() const;) + }; uint const SpaceManager::_small_chunk_limit = 4; @@ -1657,6 +1704,7 @@ ls.print("VirtualSpaceNode::take_from_committed() not available " SIZE_FORMAT " words ", chunk_word_size); // Dump some information about the virtual space that is nearly full print_on(&ls); + ls.cr(); // ~LogStream does not autoflush. } return NULL; } @@ -1703,6 +1751,7 @@ if (result) { log_trace(gc, metaspace, freelist)("Expanded %s virtual space list node by " SIZE_FORMAT " words.", (is_class() ? "class" : "non-class"), commit); + DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_committed_space_expanded)); } else { log_trace(gc, metaspace, freelist)("Failed to expand %s virtual space list node by " SIZE_FORMAT " words.", (is_class() ? "class" : "non-class"), commit); @@ -1762,15 +1811,22 @@ return result; } -void VirtualSpaceNode::print_on(outputStream* st) const { - size_t used = used_words_in_vs(); - size_t capacity = capacity_words_in_vs(); +void VirtualSpaceNode::print_on(outputStream* st, size_t scale) const { + size_t used_words = used_words_in_vs(); + size_t commit_words = committed_words(); + size_t res_words = reserved_words(); VirtualSpace* vs = virtual_space(); - st->print_cr(" space @ " PTR_FORMAT " " SIZE_FORMAT "K, " SIZE_FORMAT_W(3) "%% used " - "[" PTR_FORMAT ", " PTR_FORMAT ", " + + st->print("node @" PTR_FORMAT ": ", p2i(this)); + st->print("reserved="); + print_scaled_words(st, res_words, scale); + st->print(", committed="); + print_scaled_words_and_percentage(st, commit_words, res_words, scale); + st->print(", used="); + print_scaled_words_and_percentage(st, used_words, res_words, scale); + st->cr(); + st->print(" [" PTR_FORMAT ", " PTR_FORMAT ", " PTR_FORMAT ", " PTR_FORMAT ")", - p2i(vs), capacity / K, - capacity == 0 ? 0 : used * 100 / capacity, p2i(bottom()), p2i(top()), p2i(end()), p2i(vs->high_boundary())); } @@ -1992,6 +2048,7 @@ if (vsl->container_count() == 0 && vsl != current_virtual_space()) { log_trace(gc, metaspace, freelist)("Purging VirtualSpaceNode " PTR_FORMAT " (capacity: " SIZE_FORMAT ", used: " SIZE_FORMAT ").", p2i(vsl), vsl->capacity_words_in_vs(), vsl->used_words_in_vs()); + DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_vsnodes_purged)); // Unlink it from the list if (prev_vsl == vsl) { // This is the case of the current node being the first node. @@ -2139,6 +2196,7 @@ // ensure lock-free iteration sees fully initialized node OrderAccess::storestore(); link_vs(new_entry); + DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_vsnodes_created)); return true; } } @@ -2162,6 +2220,7 @@ VirtualSpaceNode* vsl = current_virtual_space(); ResourceMark rm; vsl->print_on(&ls); + ls.cr(); // ~LogStream does not autoflush. } } @@ -2287,11 +2346,14 @@ return next; } -void VirtualSpaceList::print_on(outputStream* st) const { +void VirtualSpaceList::print_on(outputStream* st, size_t scale) const { + st->print_cr(SIZE_FORMAT " nodes, current node: " PTR_FORMAT, + _virtual_space_count, p2i(_current_virtual_space)); VirtualSpaceListIterator iter(virtual_space_list()); while (iter.repeat()) { + st->cr(); VirtualSpaceNode* node = iter.get_next(); - node->print_on(st); + node->print_on(st, scale); } } @@ -2978,6 +3040,7 @@ p2i(this), p2i(chunk), chunk->word_size(), list_count); ResourceMark rm; locked_print_free_chunks(&ls); + ls.cr(); // ~LogStream does not autoflush. } return chunk; @@ -3072,80 +3135,10 @@ _humongous_dictionary.report_statistics(out); } -void ChunkManager::locked_get_statistics(ChunkManagerStatistics* stat) const { - assert_lock_strong(MetaspaceExpand_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(MetaspaceExpand_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."); +void ChunkManager::collect_statistics(ChunkManagerStatistics* out) const { + MutexLockerEx cl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); + for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { + out->chunk_stats(i).add(num_free_chunks(i), size_free_chunks_in_bytes(i) / sizeof(MetaWord)); } } @@ -3201,77 +3194,6 @@ return adjusted; } -size_t SpaceManager::sum_free_in_chunks_in_use() const { - MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag); - size_t free = 0; - for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { - Metachunk* chunk = chunks_in_use(i); - while (chunk != NULL) { - free += chunk->free_word_size(); - chunk = chunk->next(); - } - } - return free; -} - -size_t SpaceManager::sum_waste_in_chunks_in_use() const { - MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag); - size_t result = 0; - for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { - result += sum_waste_in_chunks_in_use(i); - } - - return result; -} - -size_t SpaceManager::sum_waste_in_chunks_in_use(ChunkIndex index) const { - size_t result = 0; - Metachunk* chunk = chunks_in_use(index); - // Count the free space in all the chunk but not the - // current chunk from which allocations are still being done. - while (chunk != NULL) { - if (chunk != current_chunk()) { - result += chunk->free_word_size(); - } - chunk = chunk->next(); - } - return result; -} - -size_t SpaceManager::sum_capacity_in_chunks_in_use() const { - // For CMS use "allocated_chunks_words()" which does not need the - // Metaspace lock. For the other collectors sum over the - // lists. Use both methods as a check that "allocated_chunks_words()" - // is correct. That is, sum_capacity_in_chunks() is too expensive - // to use in the product and allocated_chunks_words() should be used - // but allow for checking that allocated_chunks_words() returns the same - // value as sum_capacity_in_chunks_in_use() which is the definitive - // answer. - if (UseConcMarkSweepGC) { - return allocated_chunks_words(); - } else { - MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag); - size_t sum = 0; - for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { - Metachunk* chunk = chunks_in_use(i); - while (chunk != NULL) { - sum += chunk->word_size(); - chunk = chunk->next(); - } - } - return sum; - } -} - -size_t SpaceManager::sum_count_in_chunks_in_use() { - size_t count = 0; - for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { - count = count + sum_count_in_chunks_in_use(i); - } - - return count; -} - size_t SpaceManager::sum_count_in_chunks_in_use(ChunkIndex i) { size_t count = 0; Metachunk* chunk = chunks_in_use(i); @@ -3282,20 +3204,6 @@ return count; } - -size_t SpaceManager::sum_used_in_chunks_in_use() const { - MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag); - size_t used = 0; - for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { - Metachunk* chunk = chunks_in_use(i); - while (chunk != NULL) { - used += chunk->used_word_size(); - chunk = chunk->next(); - } - } - return used; -} - void SpaceManager::locked_print_chunks_in_use_on(outputStream* st) const { for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { @@ -3427,24 +3335,9 @@ } void SpaceManager::print_on(outputStream* st) const { - - for (ChunkIndex i = ZeroIndex; - i < NumberOfInUseLists ; - i = next_chunk_index(i) ) { - st->print_cr(" chunks_in_use " PTR_FORMAT " chunk size " SIZE_FORMAT, - p2i(chunks_in_use(i)), - chunks_in_use(i) == NULL ? 0 : chunks_in_use(i)->word_size()); - } - st->print_cr(" waste: Small " SIZE_FORMAT " Medium " SIZE_FORMAT - " Humongous " SIZE_FORMAT, - sum_waste_in_chunks_in_use(SmallIndex), - sum_waste_in_chunks_in_use(MediumIndex), - sum_waste_in_chunks_in_use(HumongousIndex)); - // block free lists - if (block_freelists() != NULL) { - st->print_cr("total in block free lists " SIZE_FORMAT, - block_freelists()->total_size()); - } + SpaceManagerStatistics stat; + add_to_statistics(&stat); // will lock _lock. + stat.print_on(st, 1*K, false); } SpaceManager::SpaceManager(Metaspace::MetadataType mdtype, @@ -3452,43 +3345,46 @@ Mutex* lock) : _mdtype(mdtype), _space_type(space_type), - _allocated_blocks_words(0), - _allocated_chunks_words(0), - _allocated_chunks_count(0), + _capacity_words(0), + _used_words(0), + _overhead_words(0), _block_freelists(NULL), _lock(lock) { initialize(); } -void SpaceManager::inc_size_metrics(size_t words) { +void SpaceManager::account_for_new_chunk(const Metachunk* new_chunk) { + assert_lock_strong(MetaspaceExpand_lock); - // Total of allocated Metachunks and allocated Metachunks count - // for each SpaceManager - _allocated_chunks_words = _allocated_chunks_words + words; - _allocated_chunks_count++; - // Global total of capacity in allocated Metachunks - MetaspaceUtils::inc_capacity(mdtype(), words); - // Global total of allocated Metablocks. - // used_words_slow() includes the overhead in each - // Metachunk so include it in the used when the - // Metachunk is first added (so only added once per - // Metachunk). - MetaspaceUtils::inc_used(mdtype(), Metachunk::overhead()); + + _capacity_words += new_chunk->word_size(); + _overhead_words += Metachunk::overhead(); + + // Adjust global counters: + MetaspaceUtils::inc_capacity(mdtype(), new_chunk->word_size()); + MetaspaceUtils::inc_overhead(mdtype(), Metachunk::overhead()); } -void SpaceManager::inc_used_metrics(size_t words) { - // Add to the per SpaceManager total - Atomic::add(words, &_allocated_blocks_words); - // Add to the global total +void SpaceManager::account_for_allocation(size_t words) { + // Note: we should be locked with the ClassloaderData-specific metaspace lock. + // We may or may not be locked with the global metaspace expansion lock. + assert_lock_strong(lock()); + + // Add to the per SpaceManager totals. This can be done non-atomically. + _used_words += words; + + // Adjust global counters. This will be done atomically. MetaspaceUtils::inc_used(mdtype(), words); } -void SpaceManager::dec_total_from_size_metrics() { - MetaspaceUtils::dec_capacity(mdtype(), allocated_chunks_words()); - MetaspaceUtils::dec_used(mdtype(), allocated_blocks_words()); - // Also deduct the overhead per Metachunk - MetaspaceUtils::dec_used(mdtype(), allocated_chunks_count() * Metachunk::overhead()); +void SpaceManager::account_for_spacemanager_death() { + + assert_lock_strong(MetaspaceExpand_lock); + + MetaspaceUtils::dec_capacity(mdtype(), _capacity_words); + MetaspaceUtils::dec_overhead(mdtype(), _overhead_words); + MetaspaceUtils::dec_used(mdtype(), _used_words); } void SpaceManager::initialize() { @@ -3501,23 +3397,16 @@ } SpaceManager::~SpaceManager() { + // This call this->_lock which can't be done while holding MetaspaceExpand_lock - assert(sum_capacity_in_chunks_in_use() == allocated_chunks_words(), - "sum_capacity_in_chunks_in_use() " SIZE_FORMAT - " allocated_chunks_words() " SIZE_FORMAT, - sum_capacity_in_chunks_in_use(), allocated_chunks_words()); + DEBUG_ONLY(verify_metrics()); MutexLockerEx fcl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); - assert(sum_count_in_chunks_in_use() == allocated_chunks_count(), - "sum_count_in_chunks_in_use() " SIZE_FORMAT - " allocated_chunks_count() " SIZE_FORMAT, - sum_count_in_chunks_in_use(), allocated_chunks_count()); - chunk_manager()->slow_locked_verify(); - dec_total_from_size_metrics(); + account_for_spacemanager_death(); Log(gc, metaspace, freelist) log; if (log.is_trace()) { @@ -3528,6 +3417,7 @@ if (block_freelists() != NULL) { block_freelists()->print_on(&ls); } + ls.cr(); // ~LogStream does not autoflush. } // Add all the chunks in use by this space manager @@ -3550,7 +3440,7 @@ } void SpaceManager::deallocate(MetaWord* p, size_t word_size) { - assert_lock_strong(_lock); + assert_lock_strong(lock()); // Allocations and deallocations are in raw_word_size size_t raw_word_size = get_allocation_word_size(word_size); // Lazily create a block_freelist @@ -3558,6 +3448,7 @@ _block_freelists = new BlockFreelist(); } block_freelists()->return_block(p, raw_word_size); + DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_deallocs)); } // Adds a chunk to the list of chunks in use. @@ -3584,17 +3475,18 @@ new_chunk->set_next(chunks_in_use(index)); set_chunks_in_use(index, new_chunk); - // Add to the running sum of capacity - inc_size_metrics(new_chunk->word_size()); + // Adjust counters. + account_for_new_chunk(new_chunk); assert(new_chunk->is_empty(), "Not ready for reuse"); Log(gc, metaspace, freelist) log; if (log.is_trace()) { - log.trace("SpaceManager::add_chunk: " SIZE_FORMAT ") ", sum_count_in_chunks_in_use()); + log.trace("SpaceManager::added chunk: "); ResourceMark rm; LogStream ls(log.trace()); new_chunk->print_on(&ls); chunk_manager()->locked_print_free_chunks(&ls); + ls.cr(); // ~LogStream does not autoflush. } } @@ -3604,7 +3496,7 @@ if (remaining_words >= BlockFreelist::min_dictionary_size()) { MetaWord* ptr = current_chunk()->allocate(remaining_words); deallocate(ptr, remaining_words); - inc_used_metrics(remaining_words); + account_for_allocation(remaining_words); } } } @@ -3632,6 +3524,9 @@ size_t raw_word_size = get_allocation_word_size(word_size); BlockFreelist* fl = block_freelists(); MetaWord* p = NULL; + + DEBUG_ONLY(if (VerifyMetaspace) verify_metrics_locked()); + // Allocation from the dictionary is expensive in the sense that // the dictionary has to be searched for a size. Don't allocate // from the dictionary until it starts to get fat. Is this @@ -3639,6 +3534,9 @@ // for allocations. Do some profiling. JJJ if (fl != NULL && fl->total_size() > allocation_from_dictionary_limit) { p = fl->get_block(raw_word_size); + if (p != NULL) { + DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_allocs_from_deallocated_blocks)); + } } if (p == NULL) { p = allocate_work(raw_word_size); @@ -3650,7 +3548,7 @@ // Returns the address of spaced allocated for "word_size". // This methods does not know about blocks (Metablocks) MetaWord* SpaceManager::allocate_work(size_t word_size) { - assert_lock_strong(_lock); + assert_lock_strong(lock()); #ifdef ASSERT if (Metadebug::test_metadata_failure()) { return NULL; @@ -3668,7 +3566,7 @@ } if (result != NULL) { - inc_used_metrics(word_size); + account_for_allocation(word_size); assert(result != (MetaWord*) chunks_in_use(MediumIndex), "Head of the list is being allocated"); } @@ -3696,162 +3594,129 @@ return; } +void SpaceManager::add_to_statistics_locked(SpaceManagerStatistics* out) const { + assert_lock_strong(lock()); + for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { + UsedChunksStatistics& chunk_stat = out->chunk_stats(i); + Metachunk* chunk = chunks_in_use(i); + while (chunk != NULL) { + chunk_stat.add_num(1); + chunk_stat.add_cap(chunk->word_size()); + chunk_stat.add_overhead(Metachunk::overhead()); + chunk_stat.add_used(chunk->used_word_size() - Metachunk::overhead()); + if (chunk != current_chunk()) { + chunk_stat.add_waste(chunk->free_word_size()); + } else { + chunk_stat.add_free(chunk->free_word_size()); + } + chunk = chunk->next(); + } + } + if (block_freelists() != NULL) { + out->add_free_blocks_info(block_freelists()->num_blocks(), block_freelists()->total_size()); + } +} + +void SpaceManager::add_to_statistics(SpaceManagerStatistics* out) const { + MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag); + add_to_statistics_locked(out); +} + #ifdef ASSERT -void SpaceManager::verify_allocated_blocks_words() { - // Verification is only guaranteed at a safepoint. - assert(SafepointSynchronize::is_at_safepoint() || !Universe::is_fully_initialized(), - "Verification can fail if the applications is running"); - assert(allocated_blocks_words() == sum_used_in_chunks_in_use(), - "allocation total is not consistent " SIZE_FORMAT - " vs " SIZE_FORMAT, - allocated_blocks_words(), sum_used_in_chunks_in_use()); +void SpaceManager::verify_metrics_locked() const { + assert_lock_strong(lock()); + + SpaceManagerStatistics stat; + add_to_statistics_locked(&stat); + + UsedChunksStatistics chunk_stats = stat.totals(); + + DEBUG_ONLY(chunk_stats.check_sanity()); + + assert_counter(_capacity_words, chunk_stats.cap(), "SpaceManager::_capacity_words"); + assert_counter(_used_words, chunk_stats.used(), "SpaceManager::_used_words"); + assert_counter(_overhead_words, chunk_stats.overhead(), "SpaceManager::_overhead_words"); } -#endif - -void SpaceManager::dump(outputStream* const out) const { - size_t curr_total = 0; - size_t waste = 0; - uint i = 0; - size_t used = 0; - size_t capacity = 0; - - // Add up statistics for all chunks in this SpaceManager. - for (ChunkIndex index = ZeroIndex; - index < NumberOfInUseLists; - index = next_chunk_index(index)) { - for (Metachunk* curr = chunks_in_use(index); - curr != NULL; - curr = curr->next()) { - out->print("%d) ", i++); - curr->print_on(out); - curr_total += curr->word_size(); - used += curr->used_word_size(); - capacity += curr->word_size(); - waste += curr->free_word_size() + curr->overhead();; - } - } - - if (log_is_enabled(Trace, gc, metaspace, freelist)) { - if (block_freelists() != NULL) block_freelists()->print_on(out); - } - - size_t free = current_chunk() == NULL ? 0 : current_chunk()->free_word_size(); - // Free space isn't wasted. - waste -= free; - - out->print_cr("total of all chunks " SIZE_FORMAT " used " SIZE_FORMAT - " free " SIZE_FORMAT " capacity " SIZE_FORMAT - " waste " SIZE_FORMAT, curr_total, used, free, capacity, waste); +void SpaceManager::verify_metrics() const { + MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag); + verify_metrics_locked(); } +#endif // ASSERT + + // MetaspaceUtils - - -size_t MetaspaceUtils::_capacity_words[] = {0, 0}; -volatile size_t MetaspaceUtils::_used_words[] = {0, 0}; - -size_t MetaspaceUtils::free_bytes(Metaspace::MetadataType mdtype) { +size_t MetaspaceUtils::_capacity_words [Metaspace:: MetadataTypeCount] = {0, 0}; +size_t MetaspaceUtils::_overhead_words [Metaspace:: MetadataTypeCount] = {0, 0}; +volatile size_t MetaspaceUtils::_used_words [Metaspace:: MetadataTypeCount] = {0, 0}; + +// Collect used metaspace statistics. This involves walking the CLDG. The resulting +// output will be the accumulated values for all live metaspaces. +// Note: method does not do any locking. +void MetaspaceUtils::collect_statistics(ClassLoaderMetaspaceStatistics* out) { + out->reset(); + ClassLoaderDataGraphMetaspaceIterator iter; + while (iter.repeat()) { + ClassLoaderMetaspace* msp = iter.get_next(); + if (msp != NULL) { + msp->add_to_statistics(out); + } + } +} + +size_t MetaspaceUtils::free_in_vs_bytes(Metaspace::MetadataType mdtype) { VirtualSpaceList* list = Metaspace::get_space_list(mdtype); return list == NULL ? 0 : list->free_bytes(); } -size_t MetaspaceUtils::free_bytes() { - return free_bytes(Metaspace::ClassType) + free_bytes(Metaspace::NonClassType); +size_t MetaspaceUtils::free_in_vs_bytes() { + return free_in_vs_bytes(Metaspace::ClassType) + free_in_vs_bytes(Metaspace::NonClassType); } +static void inc_stat_nonatomically(size_t* pstat, size_t words) { + assert_lock_strong(MetaspaceExpand_lock); + (*pstat) += words; +} + +static void dec_stat_nonatomically(size_t* pstat, size_t words) { + assert_lock_strong(MetaspaceExpand_lock); + const size_t size_now = *pstat; + assert(size_now >= words, "About to decrement counter below zero " + "(current value: " SIZE_FORMAT ", decrement value: " SIZE_FORMAT ".", + size_now, words); + *pstat = size_now - words; +} + +static void inc_stat_atomically(volatile size_t* pstat, size_t words) { + Atomic::add(words, pstat); +} + +static void dec_stat_atomically(volatile size_t* pstat, size_t words) { + const size_t size_now = *pstat; + assert(size_now >= words, "About to decrement counter below zero " + "(current value: " SIZE_FORMAT ", decrement value: " SIZE_FORMAT ".", + size_now, words); + Atomic::sub(words, pstat); +} + void MetaspaceUtils::dec_capacity(Metaspace::MetadataType mdtype, size_t words) { - assert_lock_strong(MetaspaceExpand_lock); - assert(words <= capacity_words(mdtype), - "About to decrement below 0: words " SIZE_FORMAT - " is greater than _capacity_words[%u] " SIZE_FORMAT, - words, mdtype, capacity_words(mdtype)); - _capacity_words[mdtype] -= words; + dec_stat_nonatomically(&_capacity_words[mdtype], words); } - void MetaspaceUtils::inc_capacity(Metaspace::MetadataType mdtype, size_t words) { - assert_lock_strong(MetaspaceExpand_lock); - // Needs to be atomic - _capacity_words[mdtype] += words; + inc_stat_nonatomically(&_capacity_words[mdtype], words); } - void MetaspaceUtils::dec_used(Metaspace::MetadataType mdtype, size_t words) { - assert(words <= used_words(mdtype), - "About to decrement below 0: words " SIZE_FORMAT - " is greater than _used_words[%u] " SIZE_FORMAT, - words, mdtype, used_words(mdtype)); - // For CMS deallocation of the Metaspaces occurs during the - // sweep which is a concurrent phase. Protection by the MetaspaceExpand_lock - // is not enough since allocation is on a per Metaspace basis - // and protected by the Metaspace lock. - Atomic::sub(words, &_used_words[mdtype]); + dec_stat_atomically(&_used_words[mdtype], words); } - void MetaspaceUtils::inc_used(Metaspace::MetadataType mdtype, size_t words) { - // _used_words tracks allocations for - // each piece of metadata. Those allocations are - // generally done concurrently by different application - // threads so must be done atomically. - Atomic::add(words, &_used_words[mdtype]); + inc_stat_atomically(&_used_words[mdtype], words); } - -size_t MetaspaceUtils::used_bytes_slow(Metaspace::MetadataType mdtype) { - size_t used = 0; - ClassLoaderDataGraphMetaspaceIterator iter; - while (iter.repeat()) { - ClassLoaderMetaspace* msp = iter.get_next(); - // Sum allocated_blocks_words for each metaspace - if (msp != NULL) { - used += msp->used_words_slow(mdtype); - } - } - return used * BytesPerWord; +void MetaspaceUtils::dec_overhead(Metaspace::MetadataType mdtype, size_t words) { + dec_stat_nonatomically(&_overhead_words[mdtype], words); } - -size_t MetaspaceUtils::free_bytes_slow(Metaspace::MetadataType mdtype) { - size_t free = 0; - ClassLoaderDataGraphMetaspaceIterator iter; - while (iter.repeat()) { - ClassLoaderMetaspace* msp = iter.get_next(); - if (msp != NULL) { - free += msp->free_words_slow(mdtype); - } - } - return free * BytesPerWord; -} - -size_t MetaspaceUtils::capacity_bytes_slow(Metaspace::MetadataType mdtype) { - if ((mdtype == Metaspace::ClassType) && !Metaspace::using_class_space()) { - return 0; - } - // Don't count the space in the freelists. That space will be - // added to the capacity calculation as needed. - size_t capacity = 0; - ClassLoaderDataGraphMetaspaceIterator iter; - while (iter.repeat()) { - ClassLoaderMetaspace* msp = iter.get_next(); - if (msp != NULL) { - capacity += msp->capacity_words_slow(mdtype); - } - } - return capacity * BytesPerWord; -} - -size_t MetaspaceUtils::capacity_bytes_slow() { -#ifdef PRODUCT - // Use capacity_bytes() in PRODUCT instead of this function. - guarantee(false, "Should not call capacity_bytes_slow() in the PRODUCT"); -#endif - size_t class_capacity = capacity_bytes_slow(Metaspace::ClassType); - size_t non_class_capacity = capacity_bytes_slow(Metaspace::NonClassType); - assert(capacity_bytes() == class_capacity + non_class_capacity, - "bad accounting: capacity_bytes() " SIZE_FORMAT - " class_capacity + non_class_capacity " SIZE_FORMAT - " class_capacity " SIZE_FORMAT " non_class_capacity " SIZE_FORMAT, - capacity_bytes(), class_capacity + non_class_capacity, - class_capacity, non_class_capacity); - - return class_capacity + non_class_capacity; +void MetaspaceUtils::inc_overhead(Metaspace::MetadataType mdtype, size_t words) { + inc_stat_nonatomically(&_overhead_words[mdtype], words); } size_t MetaspaceUtils::reserved_bytes(Metaspace::MetadataType mdtype) { @@ -3933,280 +3798,368 @@ } } -// Print information for class space and data space separately. -// This is almost the same as above. -void MetaspaceUtils::print_on(outputStream* out, Metaspace::MetadataType mdtype) { - size_t free_chunks_capacity_bytes = free_chunks_total_bytes(mdtype); - size_t capacity_bytes = capacity_bytes_slow(mdtype); - size_t used_bytes = used_bytes_slow(mdtype); - size_t free_bytes = free_bytes_slow(mdtype); - size_t used_and_free = used_bytes + free_bytes + - free_chunks_capacity_bytes; - out->print_cr(" Chunk accounting: (used in chunks " SIZE_FORMAT - "K + unused in chunks " SIZE_FORMAT "K + " - " capacity in free chunks " SIZE_FORMAT "K) = " SIZE_FORMAT - "K capacity in allocated chunks " SIZE_FORMAT "K", - used_bytes / K, - free_bytes / K, - free_chunks_capacity_bytes / K, - used_and_free / K, - capacity_bytes / K); - // Accounting can only be correct if we got the values during a safepoint - assert(!SafepointSynchronize::is_at_safepoint() || used_and_free == capacity_bytes, "Accounting is wrong"); -} - -// Print total fragmentation for class metaspaces -void MetaspaceUtils::print_class_waste(outputStream* out) { - assert(Metaspace::using_class_space(), "class metaspace not used"); - size_t cls_specialized_waste = 0, cls_small_waste = 0, cls_medium_waste = 0; - size_t cls_specialized_count = 0, cls_small_count = 0, cls_medium_count = 0, cls_humongous_count = 0; - ClassLoaderDataGraphMetaspaceIterator iter; - while (iter.repeat()) { - ClassLoaderMetaspace* msp = iter.get_next(); - if (msp != NULL) { - cls_specialized_waste += msp->class_vsm()->sum_waste_in_chunks_in_use(SpecializedIndex); - cls_specialized_count += msp->class_vsm()->sum_count_in_chunks_in_use(SpecializedIndex); - cls_small_waste += msp->class_vsm()->sum_waste_in_chunks_in_use(SmallIndex); - cls_small_count += msp->class_vsm()->sum_count_in_chunks_in_use(SmallIndex); - cls_medium_waste += msp->class_vsm()->sum_waste_in_chunks_in_use(MediumIndex); - cls_medium_count += msp->class_vsm()->sum_count_in_chunks_in_use(MediumIndex); - cls_humongous_count += msp->class_vsm()->sum_count_in_chunks_in_use(HumongousIndex); - } - } - out->print_cr(" class: " SIZE_FORMAT " specialized(s) " SIZE_FORMAT ", " - SIZE_FORMAT " small(s) " SIZE_FORMAT ", " - SIZE_FORMAT " medium(s) " SIZE_FORMAT ", " - "large count " SIZE_FORMAT, - cls_specialized_count, cls_specialized_waste, - cls_small_count, cls_small_waste, - cls_medium_count, cls_medium_waste, cls_humongous_count); -} - -// Print total fragmentation for data and class metaspaces separately -void MetaspaceUtils::print_waste(outputStream* out) { - size_t specialized_waste = 0, small_waste = 0, medium_waste = 0; - size_t specialized_count = 0, small_count = 0, medium_count = 0, humongous_count = 0; - - ClassLoaderDataGraphMetaspaceIterator iter; - while (iter.repeat()) { - ClassLoaderMetaspace* msp = iter.get_next(); - if (msp != NULL) { - specialized_waste += msp->vsm()->sum_waste_in_chunks_in_use(SpecializedIndex); - specialized_count += msp->vsm()->sum_count_in_chunks_in_use(SpecializedIndex); - small_waste += msp->vsm()->sum_waste_in_chunks_in_use(SmallIndex); - small_count += msp->vsm()->sum_count_in_chunks_in_use(SmallIndex); - medium_waste += msp->vsm()->sum_waste_in_chunks_in_use(MediumIndex); - medium_count += msp->vsm()->sum_count_in_chunks_in_use(MediumIndex); - humongous_count += msp->vsm()->sum_count_in_chunks_in_use(HumongousIndex); - } - } - out->print_cr("Total fragmentation waste (words) doesn't count free space"); - out->print_cr(" data: " SIZE_FORMAT " specialized(s) " SIZE_FORMAT ", " - SIZE_FORMAT " small(s) " SIZE_FORMAT ", " - SIZE_FORMAT " medium(s) " SIZE_FORMAT ", " - "large count " SIZE_FORMAT, - specialized_count, specialized_waste, small_count, - small_waste, medium_count, medium_waste, humongous_count); - if (Metaspace::using_class_space()) { - print_class_waste(out); - } -} - -class MetadataStats { -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; + outputStream* const _out; + const size_t _scale; + const bool _do_print; + const bool _break_down_by_chunktype; public: - PrintCLDMetaspaceInfoClosure(outputStream* out, size_t scale = K) - : _out(out), _scale(scale), _total_count(0), _total_anon_count(0) { } - - ~PrintCLDMetaspaceInfoClosure() { - print_summary(); + + uintx _num_loaders; + ClassLoaderMetaspaceStatistics _stats_total; + + uintx _num_loaders_by_spacetype [Metaspace::MetaspaceTypeCount]; + ClassLoaderMetaspaceStatistics _stats_by_spacetype [Metaspace::MetaspaceTypeCount]; + +public: + PrintCLDMetaspaceInfoClosure(outputStream* out, size_t scale, bool do_print, bool break_down_by_chunktype) + : _out(out), _scale(scale), _do_print(do_print), _break_down_by_chunktype(break_down_by_chunktype) + , _num_loaders(0) + { + memset(_num_loaders_by_spacetype, 0, sizeof(_num_loaders_by_spacetype)); } void do_cld(ClassLoaderData* cld) { + assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint"); - if (cld->is_unloading()) return; ClassLoaderMetaspace* 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()); + // Collect statistics for this class loader metaspace + ClassLoaderMetaspaceStatistics this_cld_stat; + msp->add_to_statistics(&this_cld_stat); + + // And add it to the running totals + _stats_total.add(this_cld_stat); + _num_loaders ++; + _stats_by_spacetype[msp->space_type()].add(this_cld_stat); + _num_loaders_by_spacetype[msp->space_type()] ++; + + // Optionally, print. + if (_do_print) { + + _out->print(UINTX_FORMAT_W(4) ": ", _num_loaders); + + if (cld->is_anonymous()) { + _out->print("ClassLoaderData " PTR_FORMAT " for anonymous class", p2i(cld)); + } else { + ResourceMark rm; + _out->print("ClassLoaderData " PTR_FORMAT " for %s", p2i(cld), cld->loader_name()); + } + + if (cld->is_unloading()) { + _out->print(" (unloading)"); + } + + this_cld_stat.print_on(_out, _scale, _break_down_by_chunktype); + _out->cr(); + } - print_metaspace(msp, anonymous); - _out->cr(); - } - -private: - void print_metaspace(ClassLoaderMetaspace* msp, bool anonymous); - void print_summary() const; + } // do_cld + }; -void PrintCLDMetaspaceInfoClosure::print_metaspace(ClassLoaderMetaspace* 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); +void MetaspaceUtils::print_vs(outputStream* out, size_t scale) { + const size_t reserved_nonclass_words = reserved_bytes(Metaspace::NonClassType) / sizeof(MetaWord); + const size_t committed_nonclass_words = committed_bytes(Metaspace::NonClassType) / sizeof(MetaWord); + { + if (Metaspace::using_class_space()) { + out->print(" Non-class space: "); + } + print_scaled_words(out, reserved_nonclass_words, scale, 7); + out->print(" reserved, "); + print_scaled_words_and_percentage(out, committed_nonclass_words, reserved_nonclass_words, scale, 7); + out->print_cr(" committed "); + + if (Metaspace::using_class_space()) { + const size_t reserved_class_words = reserved_bytes(Metaspace::ClassType) / sizeof(MetaWord); + const size_t committed_class_words = committed_bytes(Metaspace::ClassType) / sizeof(MetaWord); + out->print(" Class space: "); + print_scaled_words(out, reserved_class_words, scale, 7); + out->print(" reserved, "); + print_scaled_words_and_percentage(out, committed_class_words, reserved_class_words, scale, 7); + out->print_cr(" committed "); + + const size_t reserved_words = reserved_nonclass_words + reserved_class_words; + const size_t committed_words = committed_nonclass_words + committed_class_words; + out->print(" Both: "); + print_scaled_words(out, reserved_words, scale, 7); + out->print(" reserved, "); + print_scaled_words_and_percentage(out, committed_words, reserved_words, scale, 7); + out->print_cr(" committed "); + } + } +} + +// This will print out a basic metaspace usage report but +// unlike print_report() is guaranteed not to lock or to walk the CLDG. +void MetaspaceUtils::print_basic_report(outputStream* out, size_t scale) { + + out->cr(); + out->print_cr("Usage:"); 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(" Non-class: "); + } + + // In its most basic form, we do not require walking the CLDG. Instead, just print the running totals from + // MetaspaceUtils. + const size_t cap_nc = MetaspaceUtils::capacity_words(Metaspace::NonClassType); + const size_t overhead_nc = MetaspaceUtils::overhead_words(Metaspace::NonClassType); + const size_t used_nc = MetaspaceUtils::used_words(Metaspace::NonClassType); + const size_t free_and_waste_nc = cap_nc - overhead_nc - used_nc; + + print_scaled_words(out, cap_nc, scale, 5); + out->print(" capacity, "); + print_scaled_words_and_percentage(out, used_nc, cap_nc, scale, 5); + out->print(" used, "); + print_scaled_words_and_percentage(out, free_and_waste_nc, cap_nc, scale, 5); + out->print(" free+waste, "); + print_scaled_words_and_percentage(out, overhead_nc, cap_nc, scale, 5); + out->print(" overhead. "); + out->cr(); + + if (Metaspace::using_class_space()) { + const size_t cap_c = MetaspaceUtils::capacity_words(Metaspace::ClassType); + const size_t overhead_c = MetaspaceUtils::overhead_words(Metaspace::ClassType); + const size_t used_c = MetaspaceUtils::used_words(Metaspace::ClassType); + const size_t free_and_waste_c = cap_c - overhead_c - used_c; + out->print(" Class: "); + print_scaled_words(out, cap_c, scale, 5); + out->print(" capacity, "); + print_scaled_words_and_percentage(out, used_c, cap_c, scale, 5); + out->print(" used, "); + print_scaled_words_and_percentage(out, free_and_waste_c, cap_c, scale, 5); + out->print(" free+waste, "); + print_scaled_words_and_percentage(out, overhead_c, cap_c, scale, 5); + out->print(" overhead. "); + out->cr(); + + out->print(" Both: "); + const size_t cap = cap_nc + cap_c; + + print_scaled_words(out, cap, scale, 5); + out->print(" capacity, "); + print_scaled_words_and_percentage(out, used_nc + used_c, cap, scale, 5); + out->print(" used, "); + print_scaled_words_and_percentage(out, free_and_waste_nc + free_and_waste_c, cap, scale, 5); + out->print(" free+waste, "); + print_scaled_words_and_percentage(out, overhead_nc + overhead_c, cap, scale, 5); + out->print(" overhead. "); + out->cr(); + } + + out->cr(); + out->print_cr("Virtual space:"); + + print_vs(out, scale); + + out->cr(); + out->print_cr("Chunk freelists:"); + + if (Metaspace::using_class_space()) { + out->print(" Non-Class: "); + } + out->print_human_readable_size(Metaspace::chunk_manager_metadata()->free_chunks_total_words(), scale); + out->cr(); + if (Metaspace::using_class_space()) { + out->print(" Class: "); + out->print_human_readable_size(Metaspace::chunk_manager_class()->free_chunks_total_words(), scale); + out->cr(); + out->print(" Both: "); + out->print_human_readable_size(Metaspace::chunk_manager_class()->free_chunks_total_words() + + Metaspace::chunk_manager_metadata()->free_chunks_total_words(), scale); + out->cr(); + } + out->cr(); + +} + +void MetaspaceUtils::print_report(outputStream* out, size_t scale, int flags) { + + const bool print_loaders = (flags & rf_show_loaders) > 0; + const bool print_by_chunktype = (flags & rf_break_down_by_chunktype) > 0; + const bool print_by_spacetype = (flags & rf_break_down_by_spacetype) > 0; + + // Some report options require walking the class loader data graph. + PrintCLDMetaspaceInfoClosure cl(out, scale, print_loaders, print_by_chunktype); + if (print_loaders) { + out->cr(); + out->print_cr("Usage per loader:"); + out->cr(); + } + + ClassLoaderDataGraph::cld_do(&cl); // collect data and optionally print + + // Print totals, broken up by space type. + if (print_by_spacetype) { + out->cr(); + out->print_cr("Usage per space type:"); + out->cr(); + for (int space_type = (int)Metaspace::ZeroMetaspaceType; + space_type < (int)Metaspace::MetaspaceTypeCount; space_type ++) + { + uintx num = cl._num_loaders_by_spacetype[space_type]; + out->print("%s (" UINTX_FORMAT " loader%s)%c", + space_type_name((Metaspace::MetaspaceType)space_type), + num, (num == 1 ? "" : "s"), (num > 0 ? ':' : '.')); + if (num > 0) { + cl._stats_by_spacetype[space_type].print_on(out, scale, print_by_chunktype); + } + out->cr(); } - - _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); + } + + // Print totals for in-use data: + out->cr(); + out->print_cr("Total Usage ( " UINTX_FORMAT " loader%s)%c", + cl._num_loaders, (cl._num_loaders == 1 ? "" : "s"), (cl._num_loaders > 0 ? ':' : '.')); + + cl._stats_total.print_on(out, scale, print_by_chunktype); + + // -- Print Virtual space. + out->cr(); + out->print_cr("Virtual space:"); + + print_vs(out, scale); + + // -- Print VirtualSpaceList details. + if ((flags & rf_show_vslist) > 0) { + out->cr(); + out->print_cr("Virtual space list%s:", Metaspace::using_class_space() ? "s" : ""); + + if (Metaspace::using_class_space()) { + out->print_cr(" Non-Class:"); + } + Metaspace::space_list()->print_on(out, scale); + if (Metaspace::using_class_space()) { + out->print_cr(" Class:"); + Metaspace::class_space_list()->print_on(out, scale); + } + } + out->cr(); + + // -- Print VirtualSpaceList map. + if ((flags & rf_show_vsmap) > 0) { + out->cr(); + out->print_cr("Virtual space map:"); + + if (Metaspace::using_class_space()) { + out->print_cr(" Non-Class:"); + } + Metaspace::space_list()->print_map(out); + if (Metaspace::using_class_space()) { + out->print_cr(" Class:"); + Metaspace::class_space_list()->print_map(out); + } + } + out->cr(); + + // -- Print Freelists (ChunkManager) details + out->cr(); + out->print_cr("Chunk freelist%s:", Metaspace::using_class_space() ? "s" : ""); + + ChunkManagerStatistics non_class_cm_stat; + Metaspace::chunk_manager_metadata()->collect_statistics(&non_class_cm_stat); 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); + out->print_cr(" Non-Class:"); + } + non_class_cm_stat.print_on(out, scale); if (Metaspace::using_class_space()) { - _out->print(" Class data "); - _total_anon_class.print_on(_out, _scale); - } -} - -void MetaspaceUtils::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); + ChunkManagerStatistics class_cm_stat; + Metaspace::chunk_manager_class()->collect_statistics(&class_cm_stat); + out->print_cr(" Class:"); + class_cm_stat.print_on(out, scale); + } + + // As a convenience, print a summary of common waste. + out->cr(); + out->print("Waste: "); + // For all wastages, print percentages from total. As total use the total size of memory committed for metaspace. + const size_t committed_words = committed_bytes() / BytesPerWord; + + out->print("(Percentage values refer to total committed size ("); + print_scaled_words(out, committed_words, scale); + out->print_cr(")."); + + // Print waste for in-use chunks. + UsedChunksStatistics ucs_nonclass = cl._stats_total.nonclass_sm_stats().totals(); + UsedChunksStatistics ucs_class = cl._stats_total.class_sm_stats().totals(); + UsedChunksStatistics ucs_all; + ucs_all.add(ucs_nonclass); + ucs_all.add(ucs_class); + out->print(" Waste in chunks in use: "); + print_scaled_words_and_percentage(out, ucs_all.waste(), committed_words, scale, 6); + out->cr(); + out->print(" Free in chunks in use: "); + print_scaled_words_and_percentage(out, ucs_all.free(), committed_words, scale, 6); + out->cr(); + + // Print waste in free chunks. + const size_t total_capacity_in_free_chunks = + Metaspace::chunk_manager_metadata()->free_chunks_total_words() + + (Metaspace::using_class_space() ? Metaspace::chunk_manager_class()->free_chunks_total_words() : 0); + out->print(" In free chunks: "); + print_scaled_words_and_percentage(out, total_capacity_in_free_chunks, committed_words, scale, 6); + out->cr(); + + // Print waste in deallocated blocks. + const uintx free_blocks_num = + cl._stats_total.nonclass_sm_stats().free_blocks_num() + + cl._stats_total.class_sm_stats().free_blocks_num(); + const size_t free_blocks_cap_words = + cl._stats_total.nonclass_sm_stats().free_blocks_cap_words() + + cl._stats_total.class_sm_stats().free_blocks_cap_words(); + out->print("Deallocated from chunks in use: " UINTX_FORMAT " blocks, total size ", free_blocks_num); + print_scaled_words_and_percentage(out, free_blocks_cap_words, committed_words, scale, 6); + out->cr(); + + // Print internal statistics +#ifdef ASSERT + out->cr(); + out->cr(); + out->print_cr("Internal statistics:"); + out->cr(); + out->print_cr("Number of allocations: " UINTX_FORMAT ".", g_internal_statistics.num_allocs); + out->print_cr("Number of space births: " UINTX_FORMAT ".", g_internal_statistics.num_metaspace_births); + out->print_cr("Number of space deaths: " UINTX_FORMAT ".", g_internal_statistics.num_metaspace_deaths); + out->print_cr("Number of virtual space node births: " UINTX_FORMAT ".", g_internal_statistics.num_vsnodes_created); + out->print_cr("Number of virtual space node deaths: " UINTX_FORMAT ".", g_internal_statistics.num_vsnodes_purged); + out->print_cr("Number of times virtual space nodes were expanded: " UINTX_FORMAT ".", g_internal_statistics.num_committed_space_expanded); + out->print_cr("Number of deallocations: " UINTX_FORMAT " (" UINTX_FORMAT " external).", g_internal_statistics.num_deallocs, g_internal_statistics.num_external_deallocs); + out->print_cr("Allocations from deallocated blocks: " UINTX_FORMAT ".", g_internal_statistics.num_allocs_from_deallocated_blocks); + out->cr(); +#endif + + // Print some interesting settings + out->cr(); + out->cr(); + out->print("MaxMetaspaceSize: "); + out->print_human_readable_size(MaxMetaspaceSize, scale); + out->cr(); + out->print("InitialBootClassLoaderMetaspaceSize: "); + out->print_human_readable_size(InitialBootClassLoaderMetaspaceSize, scale); + out->cr(); + + out->print("UseCompressedClassPointers: %s", UseCompressedClassPointers ? "true" : "false"); + out->cr(); 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->print("CompressedClassSpaceSize: "); + out->print_human_readable_size(CompressedClassSpaceSize, scale); } 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 MetaspaceUtils::dump(outputStream* out) { - out->print_cr("All Metaspace:"); - out->print("data space: "); print_on(out, Metaspace::NonClassType); - out->print("class space: "); print_on(out, Metaspace::ClassType); - print_waste(out); -} + +} // MetaspaceUtils::print_report() // Prints an ASCII representation of the given space. void MetaspaceUtils::print_metaspace_map(outputStream* out, Metaspace::MetadataType mdtype) { @@ -4240,53 +4193,37 @@ } } -void MetaspaceUtils::verify_capacity() { +void MetaspaceUtils::verify_metrics() { #ifdef ASSERT - size_t running_sum_capacity_bytes = capacity_bytes(); - // For purposes of the running sum of capacity, verify against capacity - size_t capacity_in_use_bytes = capacity_bytes_slow(); - assert(running_sum_capacity_bytes == capacity_in_use_bytes, - "capacity_words() * BytesPerWord " SIZE_FORMAT - " capacity_bytes_slow()" SIZE_FORMAT, - running_sum_capacity_bytes, capacity_in_use_bytes); - for (Metaspace::MetadataType i = Metaspace::ClassType; - i < Metaspace:: MetadataTypeCount; - i = (Metaspace::MetadataType)(i + 1)) { - size_t capacity_in_use_bytes = capacity_bytes_slow(i); - assert(capacity_bytes(i) == capacity_in_use_bytes, - "capacity_bytes(%u) " SIZE_FORMAT - " capacity_bytes_slow(%u)" SIZE_FORMAT, - i, capacity_bytes(i), i, capacity_in_use_bytes); - } + // Please note: there are time windows where the internal counters are out of sync with + // reality. For example, when a newly created ClassLoaderMetaspace creates its first chunk - + // the ClassLoaderMetaspace is not yet attached to its ClassLoaderData object and hence will + // not be counted when iterating the CLDG. So be careful when you call this method. + ClassLoaderMetaspaceStatistics total_stat; + collect_statistics(&total_stat); + UsedChunksStatistics nonclass_chunk_stat = total_stat.nonclass_sm_stats().totals(); + UsedChunksStatistics class_chunk_stat = total_stat.class_sm_stats().totals(); + + bool mismatch = false; + for (int i = 0; i < Metaspace::MetadataTypeCount; i ++) { + Metaspace::MetadataType mdtype = (Metaspace::MetadataType)i; + UsedChunksStatistics chunk_stat = total_stat.sm_stats(mdtype).totals(); + if (capacity_words(mdtype) != chunk_stat.cap() || + used_words(mdtype) != chunk_stat.used() || + overhead_words(mdtype) != chunk_stat.overhead()) { + mismatch = true; + tty->print_cr("MetaspaceUtils::verify_metrics: counter mismatch for mdtype=%u:", mdtype); + tty->print_cr("Expected cap " SIZE_FORMAT ", used " SIZE_FORMAT ", overhead " SIZE_FORMAT ".", + capacity_words(mdtype), used_words(mdtype), overhead_words(mdtype)); + tty->print_cr("Got cap " SIZE_FORMAT ", used " SIZE_FORMAT ", overhead " SIZE_FORMAT ".", + chunk_stat.cap(), chunk_stat.used(), chunk_stat.overhead()); + tty->flush(); + } + } + assert(mismatch == false, "MetaspaceUtils::verify_metrics: counter mismatch."); #endif } -void MetaspaceUtils::verify_used() { -#ifdef ASSERT - size_t running_sum_used_bytes = used_bytes(); - // For purposes of the running sum of used, verify against used - size_t used_in_use_bytes = used_bytes_slow(); - assert(used_bytes() == used_in_use_bytes, - "used_bytes() " SIZE_FORMAT - " used_bytes_slow()" SIZE_FORMAT, - used_bytes(), used_in_use_bytes); - for (Metaspace::MetadataType i = Metaspace::ClassType; - i < Metaspace:: MetadataTypeCount; - i = (Metaspace::MetadataType)(i + 1)) { - size_t used_in_use_bytes = used_bytes_slow(i); - assert(used_bytes(i) == used_in_use_bytes, - "used_bytes(%u) " SIZE_FORMAT - " used_bytes_slow(%u)" SIZE_FORMAT, - i, used_bytes(i), i, used_in_use_bytes); - } -#endif -} - -void MetaspaceUtils::verify_metrics() { - verify_capacity(); - verify_used(); -} - // Metaspace methods @@ -4485,6 +4422,7 @@ ResourceMark rm; LogStream ls(lt); print_compressed_class_space(&ls, requested_addr); + ls.cr(); // ~LogStream does not autoflush. } } @@ -4706,12 +4644,13 @@ if (loader_data->metaspace_or_null() != NULL) { LogStream ls(log.debug()); loader_data->print_value_on(&ls); + ls.cr(); // ~LogStream does not autoflush. } } LogStream ls(log.info()); - MetaspaceUtils::dump(&ls); - MetaspaceUtils::print_metaspace_map(&ls, mdtype); - ChunkManager::print_all_chunkmanagers(&ls); + // In case of an OOM, log out a short but still useful report. + MetaspaceUtils::print_basic_report(&ls, 0); + ls.cr(); // ~LogStream does not autoflush. } bool out_of_compressed_class_space = false; @@ -4786,16 +4725,23 @@ // ClassLoaderMetaspace -ClassLoaderMetaspace::ClassLoaderMetaspace(Mutex* lock, Metaspace::MetaspaceType type) { +ClassLoaderMetaspace::ClassLoaderMetaspace(Mutex* lock, Metaspace::MetaspaceType type) + : _lock(lock) + , _space_type(type) + , _vsm(NULL) + , _class_vsm(NULL) +{ initialize(lock, type); } ClassLoaderMetaspace::~ClassLoaderMetaspace() { + DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_metaspace_deaths)); delete _vsm; if (Metaspace::using_class_space()) { delete _class_vsm; } } + void ClassLoaderMetaspace::initialize_first_chunk(Metaspace::MetaspaceType type, Metaspace::MetadataType mdtype) { Metachunk* chunk = get_initialization_chunk(type, mdtype); if (chunk != NULL) { @@ -4821,6 +4767,8 @@ void ClassLoaderMetaspace::initialize(Mutex* lock, Metaspace::MetaspaceType type) { Metaspace::verify_global_initialization(); + DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_metaspace_births)); + // Allocate SpaceManager for metadata objects. _vsm = new SpaceManager(Metaspace::NonClassType, type, lock); @@ -4842,6 +4790,9 @@ MetaWord* ClassLoaderMetaspace::allocate(size_t word_size, Metaspace::MetadataType mdtype) { Metaspace::assert_not_frozen(); + + DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_allocs)); + // Don't use class_vsm() unless UseCompressedClassPointers is true. if (Metaspace::is_class_space_allocation(mdtype)) { return class_vsm()->allocate(word_size); @@ -4877,52 +4828,14 @@ return res; } -size_t ClassLoaderMetaspace::used_words_slow(Metaspace::MetadataType mdtype) const { - if (mdtype == Metaspace::ClassType) { - return Metaspace::using_class_space() ? class_vsm()->sum_used_in_chunks_in_use() : 0; - } else { - return vsm()->sum_used_in_chunks_in_use(); // includes overhead! - } +size_t ClassLoaderMetaspace::allocated_blocks_bytes() const { + return (vsm()->used_words() + + (Metaspace::using_class_space() ? class_vsm()->used_words() : 0)) * BytesPerWord; } -size_t ClassLoaderMetaspace::free_words_slow(Metaspace::MetadataType mdtype) const { - Metaspace::assert_not_frozen(); - if (mdtype == Metaspace::ClassType) { - return Metaspace::using_class_space() ? class_vsm()->sum_free_in_chunks_in_use() : 0; - } else { - return vsm()->sum_free_in_chunks_in_use(); - } -} - -// Space capacity in the Metaspace. It includes -// space in the list of chunks from which allocations -// have been made. Don't include space in the global freelist and -// in the space available in the dictionary which -// is already counted in some chunk. -size_t ClassLoaderMetaspace::capacity_words_slow(Metaspace::MetadataType mdtype) const { - if (mdtype == Metaspace::ClassType) { - return Metaspace::using_class_space() ? class_vsm()->sum_capacity_in_chunks_in_use() : 0; - } else { - return vsm()->sum_capacity_in_chunks_in_use(); - } -} - -size_t ClassLoaderMetaspace::used_bytes_slow(Metaspace::MetadataType mdtype) const { - return used_words_slow(mdtype) * BytesPerWord; -} - -size_t ClassLoaderMetaspace::capacity_bytes_slow(Metaspace::MetadataType mdtype) const { - return capacity_words_slow(mdtype) * BytesPerWord; -} - -size_t ClassLoaderMetaspace::allocated_blocks_bytes() const { - return vsm()->allocated_blocks_bytes() + - (Metaspace::using_class_space() ? class_vsm()->allocated_blocks_bytes() : 0); -} - size_t ClassLoaderMetaspace::allocated_chunks_bytes() const { - return vsm()->allocated_chunks_bytes() + - (Metaspace::using_class_space() ? class_vsm()->allocated_chunks_bytes() : 0); + return (vsm()->capacity_words() + + (Metaspace::using_class_space() ? class_vsm()->capacity_words() : 0)) * BytesPerWord; } void ClassLoaderMetaspace::deallocate(MetaWord* ptr, size_t word_size, bool is_class) { @@ -4930,6 +4843,8 @@ assert(!SafepointSynchronize::is_at_safepoint() || Thread::current()->is_VM_thread(), "should be the VM thread"); + DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_external_deallocs)); + MutexLockerEx ml(vsm()->lock(), Mutex::_no_safepoint_check_flag); if (is_class && Metaspace::using_class_space()) { @@ -4961,16 +4876,18 @@ } } -void ClassLoaderMetaspace::dump(outputStream* const out) const { - out->print_cr("\nVirtual space manager: " INTPTR_FORMAT, p2i(vsm())); - vsm()->dump(out); +void ClassLoaderMetaspace::add_to_statistics_locked(ClassLoaderMetaspaceStatistics* out) const { + assert_lock_strong(lock()); + vsm()->add_to_statistics_locked(&out->nonclass_sm_stats()); if (Metaspace::using_class_space()) { - out->print_cr("\nClass space manager: " INTPTR_FORMAT, p2i(class_vsm())); - class_vsm()->dump(out); + class_vsm()->add_to_statistics_locked(&out->class_sm_stats()); } } - +void ClassLoaderMetaspace::add_to_statistics(ClassLoaderMetaspaceStatistics* out) const { + MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag); + add_to_statistics_locked(out); +} #ifdef ASSERT static void do_verify_chunk(Metachunk* chunk) { @@ -5316,12 +5233,12 @@ extern void test_metaspace_retrieve_chunkmanager_statistics(Metaspace::MetadataType mdType, chunkmanager_statistics_t* out) { ChunkManager* const chunk_manager = Metaspace::get_chunk_manager(mdType); - ChunkManager::ChunkManagerStatistics stat; - chunk_manager->get_statistics(&stat); - out->num_specialized_chunks = (int)stat.num_by_type[SpecializedIndex]; - out->num_small_chunks = (int)stat.num_by_type[SmallIndex]; - out->num_medium_chunks = (int)stat.num_by_type[MediumIndex]; - out->num_humongous_chunks = (int)stat.num_humongous_chunks; + ChunkManagerStatistics stat; + chunk_manager->collect_statistics(&stat); + out->num_specialized_chunks = (int)stat.chunk_stats(SpecializedIndex).num(); + out->num_small_chunks = (int)stat.chunk_stats(SmallIndex).num(); + out->num_medium_chunks = (int)stat.chunk_stats(MediumIndex).num(); + out->num_humongous_chunks = (int)stat.chunk_stats(HumongousIndex).num(); } struct chunk_geometry_t { diff --git a/src/hotspot/share/memory/metaspace.hpp b/src/hotspot/share/memory/metaspace.hpp --- a/src/hotspot/share/memory/metaspace.hpp +++ b/src/hotspot/share/memory/metaspace.hpp @@ -68,6 +68,11 @@ class VirtualSpaceList; class CollectedHeap; +namespace metaspace { +namespace internals { + class ClassLoaderMetaspaceStatistics; +}} + // Metaspaces each have a SpaceManager and allocations // are done by the SpaceManager. Allocations are done // out of the current Metachunk. When the current Metachunk @@ -94,10 +99,14 @@ MetadataTypeCount }; enum MetaspaceType { - StandardMetaspaceType, - BootMetaspaceType, - AnonymousMetaspaceType, - ReflectionMetaspaceType + ZeroMetaspaceType = 0, + //... + StandardMetaspaceType = ZeroMetaspaceType, + BootMetaspaceType = StandardMetaspaceType + 1, + AnonymousMetaspaceType = BootMetaspaceType + 1, + ReflectionMetaspaceType = AnonymousMetaspaceType + 1, + //.. + MetaspaceTypeCount }; private: @@ -193,7 +202,6 @@ static MetaWord* allocate(ClassLoaderData* loader_data, size_t word_size, MetaspaceObj::Type type, TRAPS); - void deallocate(MetaWord* ptr, size_t byte_size, bool is_class); static bool contains(const void* ptr); static bool contains_non_shared(const void* ptr); @@ -238,96 +246,97 @@ void initialize_first_chunk(Metaspace::MetaspaceType type, Metaspace::MetadataType mdtype); Metachunk* get_initialization_chunk(Metaspace::MetaspaceType type, Metaspace::MetadataType mdtype); + const Metaspace::MetaspaceType _space_type; + Mutex* const _lock; SpaceManager* _vsm; + SpaceManager* _class_vsm; + SpaceManager* vsm() const { return _vsm; } - - SpaceManager* _class_vsm; SpaceManager* class_vsm() const { return _class_vsm; } SpaceManager* get_space_manager(Metaspace::MetadataType mdtype) { assert(mdtype != Metaspace::MetadataTypeCount, "MetadaTypeCount can't be used as mdtype"); return mdtype == Metaspace::ClassType ? class_vsm() : vsm(); } + Mutex* lock() const { return _lock; } + MetaWord* expand_and_allocate(size_t size, Metaspace::MetadataType mdtype); size_t class_chunk_size(size_t word_size); + // Adds to the given statistic object. Must be locked with CLD metaspace lock. + void add_to_statistics_locked(metaspace::internals::ClassLoaderMetaspaceStatistics* out) const; + public: ClassLoaderMetaspace(Mutex* lock, Metaspace::MetaspaceType type); ~ClassLoaderMetaspace(); + Metaspace::MetaspaceType space_type() const { return _space_type; } + // Allocate space for metadata of type mdtype. This is space // within a Metachunk and is used by // allocate(ClassLoaderData*, size_t, bool, MetadataType, TRAPS) MetaWord* allocate(size_t word_size, Metaspace::MetadataType mdtype); - size_t used_words_slow(Metaspace::MetadataType mdtype) const; - size_t free_words_slow(Metaspace::MetadataType mdtype) const; - size_t capacity_words_slow(Metaspace::MetadataType mdtype) const; - - size_t used_bytes_slow(Metaspace::MetadataType mdtype) const; - size_t capacity_bytes_slow(Metaspace::MetadataType mdtype) const; - size_t allocated_blocks_bytes() const; size_t allocated_chunks_bytes() const; void deallocate(MetaWord* ptr, size_t byte_size, bool is_class); - void dump(outputStream* const out) const; - void print_on(outputStream* st) const; // Debugging support void verify(); + // Adds to the given statistic object. Will lock with CLD metaspace lock. + void add_to_statistics(metaspace::internals::ClassLoaderMetaspaceStatistics* out) const; + }; // ClassLoaderMetaspace +class MetaspaceUtils : AllStatic { -class MetaspaceUtils : AllStatic { + // Spacemanager updates running counters. + friend class SpaceManager; + + // Running counters for statistics concerning in-use chunks. + // Note: capacity = used + free + waste + overhead. Note that we do not + // count free and waste. Their sum can be deduces from the three other values. + // For more details, one should call print_report() from within a safe point. + static size_t _capacity_words [Metaspace:: MetadataTypeCount]; + static size_t _overhead_words [Metaspace:: MetadataTypeCount]; + static volatile size_t _used_words [Metaspace:: MetadataTypeCount]; + + // Atomically decrement or increment in-use statistic counters + static void dec_capacity(Metaspace::MetadataType mdtype, size_t words); + static void inc_capacity(Metaspace::MetadataType mdtype, size_t words); + static void dec_used(Metaspace::MetadataType mdtype, size_t words); + static void inc_used(Metaspace::MetadataType mdtype, size_t words); + static void dec_overhead(Metaspace::MetadataType mdtype, size_t words); + static void inc_overhead(Metaspace::MetadataType mdtype, size_t words); + + + // Getters for the in-use counters. + static size_t capacity_words(Metaspace::MetadataType mdtype) { return _capacity_words[mdtype]; } + static size_t used_words(Metaspace::MetadataType mdtype) { return _used_words[mdtype]; } + static size_t overhead_words(Metaspace::MetadataType mdtype) { return _overhead_words[mdtype]; } + static size_t free_chunks_total_words(Metaspace::MetadataType mdtype); - // These methods iterate over the classloader data graph - // for the given Metaspace type. These are slow. - static size_t used_bytes_slow(Metaspace::MetadataType mdtype); - static size_t free_bytes_slow(Metaspace::MetadataType mdtype); - static size_t capacity_bytes_slow(Metaspace::MetadataType mdtype); - static size_t capacity_bytes_slow(); + // Helper for print_xx_report. + static void print_vs(outputStream* out, size_t scale); - // Running sum of space in all Metachunks that has been - // allocated to a Metaspace. This is used instead of - // iterating over all the classloaders. One for each - // type of Metadata - static size_t _capacity_words[Metaspace:: MetadataTypeCount]; - // Running sum of space in all Metachunks that - // are being used for metadata. One for each - // type of Metadata. - static volatile size_t _used_words[Metaspace:: MetadataTypeCount]; +public: - public: - // Decrement and increment _allocated_capacity_words - static void dec_capacity(Metaspace::MetadataType type, size_t words); - static void inc_capacity(Metaspace::MetadataType type, size_t words); - - // Decrement and increment _allocated_used_words - static void dec_used(Metaspace::MetadataType type, size_t words); - static void inc_used(Metaspace::MetadataType type, size_t words); - - // Total of space allocated to metadata in all Metaspaces. - // This sums the space used in each Metachunk by - // iterating over the classloader data graph - static size_t used_bytes_slow() { - return used_bytes_slow(Metaspace::ClassType) + - used_bytes_slow(Metaspace::NonClassType); - } + // Collect used metaspace statistics. This involves walking the CLDG. The resulting + // output will be the accumulated values for all live metaspaces. + // Note: method does not do any locking. + static void collect_statistics(metaspace::internals::ClassLoaderMetaspaceStatistics* out); // Used by MetaspaceCounters static size_t free_chunks_total_words(); static size_t free_chunks_total_bytes(); static size_t free_chunks_total_bytes(Metaspace::MetadataType mdtype); - static size_t capacity_words(Metaspace::MetadataType mdtype) { - return _capacity_words[mdtype]; - } static size_t capacity_words() { return capacity_words(Metaspace::NonClassType) + capacity_words(Metaspace::ClassType); @@ -339,9 +348,6 @@ return capacity_words() * BytesPerWord; } - static size_t used_words(Metaspace::MetadataType mdtype) { - return _used_words[mdtype]; - } static size_t used_words() { return used_words(Metaspace::NonClassType) + used_words(Metaspace::ClassType); @@ -353,8 +359,9 @@ return used_words() * BytesPerWord; } - static size_t free_bytes(); - static size_t free_bytes(Metaspace::MetadataType mdtype); + // Space committed but yet unclaimed by any class loader. + static size_t free_in_vs_bytes(); + static size_t free_in_vs_bytes(Metaspace::MetadataType mdtype); static size_t reserved_bytes(Metaspace::MetadataType mdtype); static size_t reserved_bytes() { @@ -373,7 +380,29 @@ return min_chunk_size_words() * BytesPerWord; } - static void print_metadata_for_nmt(outputStream* out, size_t scale = K); + // Flags for print_report(). + enum ReportFlag { + // Show usage by class loader. + rf_show_loaders = (1 << 0), + // Breaks report down by chunk type (small, medium, ...). + rf_break_down_by_chunktype = (1 << 1), + // Breaks report down by space type (anonymous, reflection, ...). + rf_break_down_by_spacetype = (1 << 2), + // Print details about the underlying virtual spaces. + rf_show_vslist = (1 << 3), + // Print metaspace map. + rf_show_vsmap = (1 << 4) + }; + + // This will print out a basic metaspace usage report but + // unlike print_report() is guaranteed not to lock or to walk the CLDG. + static void print_basic_report(outputStream* st, size_t scale); + + // Prints a report about the current metaspace state. + // Optional parts can be enabled via flags. + // Function will walk the CLDG and will lock the expand lock; if that is not + // convenient, use print_basic_report() instead. + static void print_report(outputStream* out, size_t scale = 0, int flags = 0); static bool has_chunk_free_list(Metaspace::MetadataType mdtype); static MetaspaceChunkFreeListSummary chunk_free_list_summary(Metaspace::MetadataType mdtype); @@ -381,20 +410,13 @@ // Print change in used metadata. static void print_metaspace_change(size_t prev_metadata_used); static void print_on(outputStream * out); - static void print_on(outputStream * out, Metaspace::MetadataType mdtype); - - static void print_class_waste(outputStream* out); - static void print_waste(outputStream* out); // Prints an ASCII representation of the given space. static void print_metaspace_map(outputStream* out, Metaspace::MetadataType mdtype); static void dump(outputStream* out); static void verify_free_chunks(); - // Checks that the values returned by allocated_capacity_bytes() and - // capacity_bytes_slow() are the same. - static void verify_capacity(); - static void verify_used(); + // Check internal counters (capacity, used). static void verify_metrics(); }; diff --git a/src/hotspot/share/memory/metaspace/metaspaceCommon.cpp b/src/hotspot/share/memory/metaspace/metaspaceCommon.cpp new file mode 100644 --- /dev/null +++ b/src/hotspot/share/memory/metaspace/metaspaceCommon.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, SAP 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 "memory/metaspace/metaspaceCommon.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/ostream.hpp" + +namespace metaspace { +namespace internals { + +// Print a size, in words, scaled. +void print_scaled_words(outputStream* st, size_t word_size, size_t scale, int width) { + st->print_human_readable_size(word_size * sizeof(MetaWord), scale, width); +} + +// Convenience helper: prints a size value and a percentage. +void print_scaled_words_and_percentage(outputStream* st, size_t word_size, size_t compare_word_size, size_t scale, int width) { + print_scaled_words(st, word_size, scale, width); + st->print(" ("); + st->print_percentage(compare_word_size, word_size); + st->print(")"); +} + +} // namespace internals +} // namespace metaspace diff --git a/src/hotspot/share/memory/metaspace/metaspaceCommon.hpp b/src/hotspot/share/memory/metaspace/metaspaceCommon.hpp new file mode 100644 --- /dev/null +++ b/src/hotspot/share/memory/metaspace/metaspaceCommon.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018, 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_MEMORY_METASPACE_METASPACECOMMON_HPP_ +#define SHARE_MEMORY_METASPACE_METASPACECOMMON_HPP_ + +#include "utilities/globalDefinitions.hpp" + + +class outputStream; + +namespace metaspace { +namespace internals { + +// Print a size, in words, scaled. +void print_scaled_words(outputStream* st, size_t word_size, size_t scale = 0, int width = -1); + +// Convenience helper: prints a size value and a percentage. +void print_scaled_words_and_percentage(outputStream* st, size_t word_size, size_t compare_word_size, size_t scale = 0, int width = -1); + +} // namespace internals +} // namespace metaspace + +#endif /* SHARE_MEMORY_METASPACE_METASPACESTATISTICS_HPP_ */ diff --git a/src/hotspot/share/memory/metaspace/metaspaceDCmd.cpp b/src/hotspot/share/memory/metaspace/metaspaceDCmd.cpp new file mode 100644 --- /dev/null +++ b/src/hotspot/share/memory/metaspace/metaspaceDCmd.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2018, 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 "memory/metaspace.hpp" +#include "memory/metaspace/metaspaceDCmd.hpp" +#include "memory/resourceArea.hpp" +#include "services/diagnosticCommand.hpp" +#include "services/nmtCommon.hpp" + +namespace metaspace { + +MetaspaceDCmd::MetaspaceDCmd(outputStream* output, bool heap) + : DCmdWithParser(output, heap) + , _basic("basic", "Prints a basic summary (does not need a safepoint).", "BOOLEAN", false, "false") + , _show_loaders("show-loaders", "Shows usage by class loader.", "BOOLEAN", false, "false") + , _by_chunktype("by-chunktype", "Break down numbers by chunk type.", "BOOLEAN", false, "false") + , _by_spacetype("by-spacetype", "Break down numbers by loader type.", "BOOLEAN", false, "false") + , _show_vslist("vslist", "Shows details about the underlying virtual space.", "BOOLEAN", false, "false") + , _show_vsmap("vsmap", "Shows chunk composition of the underlying virtual spaces", "BOOLEAN", false, "false") + , _scale("scale", "Memory usage in which to scale. Valid values are: 1, KB, MB or GB (fixed scale) " + "or \"dynamic\" for a dynamically choosen scale.", + "STRING", false, "dynamic") +{ + _dcmdparser.add_dcmd_option(&_basic); + _dcmdparser.add_dcmd_option(&_show_loaders); + _dcmdparser.add_dcmd_option(&_by_chunktype); + _dcmdparser.add_dcmd_option(&_by_spacetype); + _dcmdparser.add_dcmd_option(&_show_vslist); + _dcmdparser.add_dcmd_option(&_show_vsmap); + _dcmdparser.add_dcmd_option(&_scale); +} + +int MetaspaceDCmd::num_arguments() { + ResourceMark rm; + MetaspaceDCmd* dcmd = new MetaspaceDCmd(NULL, false); + if (dcmd != NULL) { + DCmdMark mark(dcmd); + return dcmd->_dcmdparser.num_arguments(); + } else { + return 0; + } +} + +void MetaspaceDCmd::execute(DCmdSource source, TRAPS) { + // Parse scale value. + const char* scale_value = _scale.value(); + size_t scale = 0; + if (scale_value != NULL) { + if (strcasecmp("dynamic", scale_value) == 0) { + scale = 0; + } else { + scale = NMTUtil::scale_from_name(scale_value); + if (scale == 0) { + output()->print_cr("Invalid scale: \"%s\". Will use dynamic scaling.", scale_value); + } + } + } + if (_basic.value() == true) { + if (_show_loaders.value() || _by_chunktype.value() || _by_spacetype.value() || + _show_vslist.value() || _show_vsmap.value()) { + // Basic mode. Just print essentials. Does not need to be at a safepoint. + output()->print_cr("In basic mode, additional arguments are ignored."); + } + MetaspaceUtils::print_basic_report(output(), scale); + } else { + // Full mode. Requires safepoint. + int flags = 0; + if (_show_loaders.value()) flags |= MetaspaceUtils::rf_show_loaders; + if (_by_chunktype.value()) flags |= MetaspaceUtils::rf_break_down_by_chunktype; + if (_by_spacetype.value()) flags |= MetaspaceUtils::rf_break_down_by_spacetype; + if (_show_vslist.value()) flags |= MetaspaceUtils::rf_show_vslist; + if (_show_vsmap.value()) flags |= MetaspaceUtils::rf_show_vsmap; + VM_PrintMetadata op(output(), scale, flags); + VMThread::execute(&op); + } +} + +} // namespace metaspace + diff --git a/src/hotspot/share/memory/metaspace/metaspaceDCmd.hpp b/src/hotspot/share/memory/metaspace/metaspaceDCmd.hpp new file mode 100644 --- /dev/null +++ b/src/hotspot/share/memory/metaspace/metaspaceDCmd.hpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018, 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_MEMORY_METASPACE_METASPACEDCMD_HPP_ +#define SHARE_MEMORY_METASPACE_METASPACEDCMD_HPP_ + +#include "services/diagnosticCommand.hpp" + +class outputStream; + +namespace metaspace { + +class MetaspaceDCmd : public DCmdWithParser { + DCmdArgument _basic; + DCmdArgument _show_loaders; + DCmdArgument _by_spacetype; + DCmdArgument _by_chunktype; + DCmdArgument _show_vslist; + DCmdArgument _show_vsmap; + DCmdArgument _scale; +public: + MetaspaceDCmd(outputStream* output, bool heap); + static const char* name() { + return "VM.metaspace"; + } + static const char* description() { + return "Prints the statistics for the metaspace"; + } + static const char* impact() { + return "Medium: Depends on number of classes loaded."; + } + static const JavaPermission permission() { + JavaPermission p = {"java.lang.management.ManagementPermission", + "monitor", NULL}; + return p; + } + static int num_arguments(); + virtual void execute(DCmdSource source, TRAPS); +}; + +} // namespace metaspace + +#endif /* SHARE_MEMORY_METASPACE_METASPACESTATISTICS_HPP_ */ diff --git a/src/hotspot/share/memory/metaspace/metaspaceStatistics.cpp b/src/hotspot/share/memory/metaspace/metaspaceStatistics.cpp new file mode 100644 --- /dev/null +++ b/src/hotspot/share/memory/metaspace/metaspaceStatistics.cpp @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, SAP 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 "memory/metachunk.hpp" +#include "memory/metaspace/metaspaceCommon.hpp" +#include "memory/metaspace/metaspaceStatistics.hpp" +#include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/ostream.hpp" + +namespace metaspace { +namespace internals { + +// FreeChunksStatistics methods + +FreeChunksStatistics::FreeChunksStatistics() +: _num(0), _cap(0) +{} + +void FreeChunksStatistics::reset() { + _num = 0; _cap = 0; +} + +void FreeChunksStatistics::add(uintx n, size_t s) { + _num += n; _cap += s; +} + +void FreeChunksStatistics::add(const FreeChunksStatistics& other) { + _num += other._num; + _cap += other._cap; +} + +void FreeChunksStatistics::print_on(outputStream* st, size_t scale) const { + st->print(UINTX_FORMAT, _num); + st->print(" chunks, total capacity "); + print_scaled_words(st, _cap, scale); +} + +// ChunkManagerStatistics methods + +void ChunkManagerStatistics::reset() { + for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { + _chunk_stats[i].reset(); + } +} + +size_t ChunkManagerStatistics::total_capacity() const { + return _chunk_stats[SpecializedIndex].cap() + + _chunk_stats[SmallIndex].cap() + + _chunk_stats[MediumIndex].cap() + + _chunk_stats[HumongousIndex].cap(); +} + +void ChunkManagerStatistics::print_on(outputStream* st, size_t scale) const { + FreeChunksStatistics totals; + for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { + st->cr(); + st->print("%12s chunks: ", chunk_size_name(i)); + if (_chunk_stats[i].num() > 0) { + st->print(UINTX_FORMAT_W(4) ", capacity ", _chunk_stats[i].num()); + print_scaled_words(st, _chunk_stats[i].cap(), scale); + } else { + st->print("(none)"); + } + totals.add(_chunk_stats[i]); + } + st->cr(); + st->print("%19s: " UINTX_FORMAT_W(4) ", capacity=", "Total", totals.num()); + print_scaled_words(st, totals.cap(), scale); + st->cr(); +} + +// UsedChunksStatistics methods + +UsedChunksStatistics::UsedChunksStatistics() +: _num(0), _cap(0), _used(0), _free(0), _waste(0), _overhead(0) +{} + +void UsedChunksStatistics::reset() { + _num = 0; + _cap = _overhead = _used = _free = _waste = 0; +} + +void UsedChunksStatistics::add(const UsedChunksStatistics& other) { + _num += other._num; + _cap += other._cap; + _used += other._used; + _free += other._free; + _waste += other._waste; + _overhead += other._overhead; + DEBUG_ONLY(check_sanity()); +} + +void UsedChunksStatistics::print_on(outputStream* st, size_t scale) const { + int col = st->position(); + st->print(UINTX_FORMAT_W(4) " chunk%s, ", _num, _num != 1 ? "s" : ""); + if (_num > 0) { + col += 14; st->fill_to(col); + + print_scaled_words(st, _cap, scale, 5); + st->print(" capacity, "); + + col += 18; st->fill_to(col); + print_scaled_words_and_percentage(st, _used, _cap, scale, 5); + st->print(" used, "); + + col += 20; st->fill_to(col); + print_scaled_words_and_percentage(st, _free, _cap, scale, 5); + st->print(" free, "); + + col += 20; st->fill_to(col); + print_scaled_words_and_percentage(st, _waste, _cap, scale, 5); + st->print(" waste, "); + + col += 20; st->fill_to(col); + print_scaled_words_and_percentage(st, _overhead, _cap, scale, 5); + st->print(" overhead"); + } + DEBUG_ONLY(check_sanity()); +} + +#ifdef ASSERT +void UsedChunksStatistics::check_sanity() const { + assert(_overhead == (Metachunk::overhead() * _num), "Sanity: Overhead."); + assert(_cap == _used + _free + _waste + _overhead, "Sanity: Capacity."); +} +#endif + +// SpaceManagerStatistics methods + +SpaceManagerStatistics::SpaceManagerStatistics() { reset(); } + +void SpaceManagerStatistics::reset() { + for (int i = 0; i < NumberOfInUseLists; i ++) { + _chunk_stats[i].reset(); + _free_blocks_num = 0; _free_blocks_cap_words = 0; + } +} + +void SpaceManagerStatistics::add_free_blocks_info(uintx num, size_t cap) { + _free_blocks_num += num; + _free_blocks_cap_words += cap; +} + +void SpaceManagerStatistics::add(const SpaceManagerStatistics& other) { + for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { + _chunk_stats[i].add(other._chunk_stats[i]); + } + _free_blocks_num += other._free_blocks_num; + _free_blocks_cap_words += other._free_blocks_cap_words; +} + +// Returns total chunk statistics over all chunk types. +UsedChunksStatistics SpaceManagerStatistics::totals() const { + UsedChunksStatistics stat; + for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { + stat.add(_chunk_stats[i]); + } + return stat; +} + +void SpaceManagerStatistics::print_on(outputStream* st, size_t scale, bool detailed) const { + streamIndentor sti(st); + if (detailed) { + st->cr_indent(); + st->print("Usage by chunk type:"); + { + streamIndentor sti2(st); + for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { + st->cr_indent(); + st->print("%15s: ", chunk_size_name(i)); + if (_chunk_stats[i].num() == 0) { + st->print(" (none)"); + } else { + _chunk_stats[i].print_on(st, scale); + } + } + + st->cr_indent(); + st->print("%15s: ", "-total-"); + totals().print_on(st, scale); + } + if (_free_blocks_num > 0) { + st->cr_indent(); + st->print("deallocated: " UINTX_FORMAT " blocks with ", _free_blocks_num); + print_scaled_words(st, _free_blocks_cap_words, scale); + } + } else { + totals().print_on(st, scale); + st->print(", "); + st->print("deallocated: " UINTX_FORMAT " blocks with ", _free_blocks_num); + print_scaled_words(st, _free_blocks_cap_words, scale); + } +} + +// ClassLoaderMetaspaceStatistics methods + +ClassLoaderMetaspaceStatistics::ClassLoaderMetaspaceStatistics() { reset(); } + +void ClassLoaderMetaspaceStatistics::reset() { + nonclass_sm_stats().reset(); + if (Metaspace::using_class_space()) { + class_sm_stats().reset(); + } +} + +// Returns total space manager statistics for both class and non-class metaspace +SpaceManagerStatistics ClassLoaderMetaspaceStatistics::totals() const { + SpaceManagerStatistics stats; + stats.add(nonclass_sm_stats()); + if (Metaspace::using_class_space()) { + stats.add(class_sm_stats()); + } + return stats; +} + +void ClassLoaderMetaspaceStatistics::add(const ClassLoaderMetaspaceStatistics& other) { + nonclass_sm_stats().add(other.nonclass_sm_stats()); + if (Metaspace::using_class_space()) { + class_sm_stats().add(other.class_sm_stats()); + } +} + +void ClassLoaderMetaspaceStatistics::print_on(outputStream* st, size_t scale, bool detailed) const { + streamIndentor sti(st); + st->cr_indent(); + if (Metaspace::using_class_space()) { + st->print("Non-Class: "); + } + nonclass_sm_stats().print_on(st, scale, detailed); + if (detailed) { + st->cr(); + } + if (Metaspace::using_class_space()) { + st->cr_indent(); + st->print(" Class: "); + class_sm_stats().print_on(st, scale, detailed); + if (detailed) { + st->cr(); + } + st->cr_indent(); + st->print(" Both: "); + totals().print_on(st, scale, detailed); + if (detailed) { + st->cr(); + } + } + st->cr(); +} + + +} // end namespace internals +} // end namespace metaspace + + diff --git a/src/hotspot/share/memory/metaspace/metaspaceStatistics.hpp b/src/hotspot/share/memory/metaspace/metaspaceStatistics.hpp new file mode 100644 --- /dev/null +++ b/src/hotspot/share/memory/metaspace/metaspaceStatistics.hpp @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, SAP 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_MEMORY_METASPACE_METASPACESTATISTICS_HPP_ +#define SHARE_MEMORY_METASPACE_METASPACESTATISTICS_HPP_ + +#include "utilities/globalDefinitions.hpp" +#include "memory/metachunk.hpp" // for ChunkIndex enum +#include "memory/metaspace.hpp" // for MetadataType enum + +class outputStream; + +namespace metaspace { +namespace internals { + +// Contains statistics for a number of free chunks. +class FreeChunksStatistics { + uintx _num; // Number of chunks + size_t _cap; // Total capacity, in words + +public: + FreeChunksStatistics(); + + void reset(); + + uintx num() const { return _num; } + size_t cap() const { return _cap; } + + void add(uintx n, size_t s); + void add(const FreeChunksStatistics& other); + void print_on(outputStream* st, size_t scale) const; + +}; // end: FreeChunksStatistics + + +// Contains statistics for a ChunkManager. +class ChunkManagerStatistics { + + FreeChunksStatistics _chunk_stats[NumberOfInUseLists]; + +public: + + // Free chunk statistics, by chunk index. + const FreeChunksStatistics& chunk_stats(ChunkIndex index) const { return _chunk_stats[index]; } + FreeChunksStatistics& chunk_stats(ChunkIndex index) { return _chunk_stats[index]; } + + void reset(); + size_t total_capacity() const; + + void print_on(outputStream* st, size_t scale) const; + +}; // ChunkManagerStatistics + +// Contains statistics for a number of chunks in use. +// Each chunk has a used and free portion; however, there are current chunks (serving +// potential future metaspace allocations) and non-current chunks. Unused portion of the +// former is counted as free, unused portion of the latter counts as waste. +class UsedChunksStatistics { + uintx _num; // Number of chunks + size_t _cap; // Total capacity in words. + size_t _used; // Total used area, in words + size_t _free; // Total free area (unused portions of current chunks), in words + size_t _waste; // Total waste area (unused portions of non-current chunks), in words + size_t _overhead; // Total sum of chunk overheads, in words. + +public: + + UsedChunksStatistics(); + + void reset(); + + uintx num() const { return _num; } + + // Total capacity, in words + size_t cap() const { return _cap; } + + // Total used area, in words + size_t used() const { return _used; } + + // Total free area (unused portions of current chunks), in words + size_t free() const { return _free; } + + // Total waste area (unused portions of non-current chunks), in words + size_t waste() const { return _waste; } + + // Total area spent in overhead (chunk headers), in words + size_t overhead() const { return _overhead; } + + void add_num(uintx n) { _num += n; } + void add_cap(size_t s) { _cap += s; } + void add_used(size_t s) { _used += s; } + void add_free(size_t s) { _free += s; } + void add_waste(size_t s) { _waste += s; } + void add_overhead(size_t s) { _overhead += s; } + + void add(const UsedChunksStatistics& other); + + void print_on(outputStream* st, size_t scale) const; + +#ifdef ASSERT + void check_sanity() const; +#endif + +}; // UsedChunksStatistics + +// Class containing statistics for one or more space managers. +class SpaceManagerStatistics { + + UsedChunksStatistics _chunk_stats[NumberOfInUseLists]; + uintx _free_blocks_num; + size_t _free_blocks_cap_words; + +public: + + SpaceManagerStatistics(); + + // Chunk statistics by chunk index + const UsedChunksStatistics& chunk_stats(ChunkIndex index) const { return _chunk_stats[index]; } + UsedChunksStatistics& chunk_stats(ChunkIndex index) { return _chunk_stats[index]; } + + uintx free_blocks_num () const { return _free_blocks_num; } + size_t free_blocks_cap_words () const { return _free_blocks_cap_words; } + + void reset(); + + void add_free_blocks_info(uintx num, size_t cap); + + // Returns total chunk statistics over all chunk types. + UsedChunksStatistics totals() const; + + void add(const SpaceManagerStatistics& other); + + void print_on(outputStream* st, size_t scale, bool detailed) const; + +}; // SpaceManagerStatistics + +class ClassLoaderMetaspaceStatistics { + + SpaceManagerStatistics _sm_stats[Metaspace::MetadataTypeCount]; + +public: + + ClassLoaderMetaspaceStatistics(); + + const SpaceManagerStatistics& sm_stats(Metaspace::MetadataType mdType) const { return _sm_stats[mdType]; } + SpaceManagerStatistics& sm_stats(Metaspace::MetadataType mdType) { return _sm_stats[mdType]; } + + const SpaceManagerStatistics& nonclass_sm_stats() const { return sm_stats(Metaspace::NonClassType); } + SpaceManagerStatistics& nonclass_sm_stats() { return sm_stats(Metaspace::NonClassType); } + const SpaceManagerStatistics& class_sm_stats() const { return sm_stats(Metaspace::ClassType); } + SpaceManagerStatistics& class_sm_stats() { return sm_stats(Metaspace::ClassType); } + + void reset(); + + void add(const ClassLoaderMetaspaceStatistics& other); + + // Returns total space manager statistics for both class and non-class metaspace + SpaceManagerStatistics totals() const; + + void print_on(outputStream* st, size_t scale, bool detailed) const; + +}; // ClassLoaderMetaspaceStatistics + +} // namespace internals +} // namespace metaspace + +#endif /* SHARE_MEMORY_METASPACE_METASPACESTATISTICS_HPP_ */ diff --git a/src/hotspot/share/runtime/vm_operations.cpp b/src/hotspot/share/runtime/vm_operations.cpp --- a/src/hotspot/share/runtime/vm_operations.cpp +++ b/src/hotspot/share/runtime/vm_operations.cpp @@ -235,7 +235,7 @@ } void VM_PrintMetadata::doit() { - MetaspaceUtils::print_metadata_for_nmt(_out, _scale); + MetaspaceUtils::print_report(_out, _scale, _flags); } VM_FindDeadlocks::~VM_FindDeadlocks() { diff --git a/src/hotspot/share/runtime/vm_operations.hpp b/src/hotspot/share/runtime/vm_operations.hpp --- a/src/hotspot/share/runtime/vm_operations.hpp +++ b/src/hotspot/share/runtime/vm_operations.hpp @@ -391,10 +391,14 @@ class VM_PrintMetadata : public VM_Operation { private: - outputStream* _out; - size_t _scale; + outputStream* const _out; + const size_t _scale; + const int _flags; + public: - VM_PrintMetadata(outputStream* out, size_t scale) : _out(out), _scale(scale) {}; + VM_PrintMetadata(outputStream* out, size_t scale, int flags) + : _out(out), _scale(scale), _flags(flags) + {}; VMOp_Type type() const { return VMOp_PrintMetadata; } void doit(); diff --git a/src/hotspot/share/services/diagnosticCommand.cpp b/src/hotspot/share/services/diagnosticCommand.cpp --- a/src/hotspot/share/services/diagnosticCommand.cpp +++ b/src/hotspot/share/services/diagnosticCommand.cpp @@ -29,6 +29,7 @@ #include "compiler/compileBroker.hpp" #include "compiler/directivesParser.hpp" #include "gc/shared/vmGCOperations.hpp" +#include "memory/metaspace/metaspaceDCmd.hpp" #include "memory/resourceArea.hpp" #include "oops/objArrayOop.inline.hpp" #include "oops/oop.inline.hpp" @@ -90,7 +91,7 @@ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); - DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); #if INCLUDE_JVMTI // Both JVMTI and SERVICES have to be enabled to have this dcmd DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); #endif // INCLUDE_JVMTI diff --git a/src/hotspot/share/services/diagnosticCommand.hpp b/src/hotspot/share/services/diagnosticCommand.hpp --- a/src/hotspot/share/services/diagnosticCommand.hpp +++ b/src/hotspot/share/services/diagnosticCommand.hpp @@ -866,25 +866,4 @@ virtual void execute(DCmdSource source, TRAPS); }; -class MetaspaceDCmd : public DCmd { -public: - MetaspaceDCmd(outputStream* output, bool heap); - static const char* name() { - return "VM.metaspace"; - } - static const char* description() { - return "Prints the statistics for the metaspace"; - } - static const char* impact() { - return "Medium: Depends on number of classes loaded."; - } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", NULL}; - return p; - } - static int num_arguments() { return 0; } - virtual void execute(DCmdSource source, TRAPS); -}; - #endif // SHARE_VM_SERVICES_DIAGNOSTICCOMMAND_HPP diff --git a/src/hotspot/share/services/memReporter.cpp b/src/hotspot/share/services/memReporter.cpp --- a/src/hotspot/share/services/memReporter.cpp +++ b/src/hotspot/share/services/memReporter.cpp @@ -201,7 +201,7 @@ size_t used = MetaspaceUtils::used_bytes(type); size_t free = (MetaspaceUtils::capacity_bytes(type) - used) + MetaspaceUtils::free_chunks_total_bytes(type) - + MetaspaceUtils::free_bytes(type); + + MetaspaceUtils::free_in_vs_bytes(type); assert(committed >= used + free, "Sanity"); size_t waste = committed - (used + free); diff --git a/src/hotspot/share/services/memTracker.cpp b/src/hotspot/share/services/memTracker.cpp --- a/src/hotspot/share/services/memTracker.cpp +++ b/src/hotspot/share/services/memTracker.cpp @@ -177,10 +177,12 @@ } else { MemDetailReporter rpt(baseline, output); rpt.report(); - + output->print("Metaspace:"); // Metadata reporting requires a safepoint, so avoid it if VM is not in good state. assert(!VMError::fatal_error_in_progress(), "Do not report metadata in error report"); - VM_PrintMetadata vmop(output, K); + VM_PrintMetadata vmop(output, K, + MetaspaceUtils::rf_show_loaders | + MetaspaceUtils::rf_break_down_by_spacetype); VMThread::execute(&vmop); } } diff --git a/src/hotspot/share/services/metaspaceDCmd.cpp b/src/hotspot/share/services/metaspaceDCmd.cpp deleted file mode 100644 --- a/src/hotspot/share/services/metaspaceDCmd.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2018, 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 "memory/metaspace.hpp" -#include "services/diagnosticCommand.hpp" - -MetaspaceDCmd::MetaspaceDCmd(outputStream* output, bool heap): DCmd(output, heap) { -} - -void MetaspaceDCmd::execute(DCmdSource source, TRAPS) { - const size_t scale = 1 * K; - VM_PrintMetadata op(output(), scale); - VMThread::execute(&op); -} - diff --git a/src/hotspot/share/services/nmtCommon.cpp b/src/hotspot/share/services/nmtCommon.cpp --- a/src/hotspot/share/services/nmtCommon.cpp +++ b/src/hotspot/share/services/nmtCommon.cpp @@ -23,6 +23,7 @@ */ #include "precompiled.hpp" #include "services/nmtCommon.hpp" +#include "utilities/globalDefinitions.hpp" const char* NMTUtil::_memory_type_names[] = { "Java Heap", @@ -59,14 +60,13 @@ size_t NMTUtil::scale_from_name(const char* scale) { assert(scale != NULL, "Null pointer check"); - if (strncmp(scale, "KB", 2) == 0 || - strncmp(scale, "kb", 2) == 0) { + if (strcasecmp(scale, "1") == 0 || strcasecmp(scale, "b") == 0) { + return 1; + } else if (strcasecmp(scale, "kb") == 0 || strcasecmp(scale, "k") == 0) { return K; - } else if (strncmp(scale, "MB", 2) == 0 || - strncmp(scale, "mb", 2) == 0) { + } else if (strcasecmp(scale, "mb") == 0 || strcasecmp(scale, "m") == 0) { return M; - } else if (strncmp(scale, "GB", 2) == 0 || - strncmp(scale, "gb", 2) == 0) { + } else if (strcasecmp(scale, "gb") == 0 || strcasecmp(scale, "g") == 0) { return G; } else { return 0; // Invalid value diff --git a/src/hotspot/share/services/virtualMemoryTracker.cpp b/src/hotspot/share/services/virtualMemoryTracker.cpp --- a/src/hotspot/share/services/virtualMemoryTracker.cpp +++ b/src/hotspot/share/services/virtualMemoryTracker.cpp @@ -516,7 +516,7 @@ size_t free_in_bytes = (MetaspaceUtils::capacity_bytes(type) - MetaspaceUtils::used_bytes(type)) + MetaspaceUtils::free_chunks_total_bytes(type) - + MetaspaceUtils::free_bytes(type); + + MetaspaceUtils::free_in_vs_bytes(type); mss._free_in_bytes[type] = free_in_bytes; } diff --git a/src/hotspot/share/utilities/ostream.cpp b/src/hotspot/share/utilities/ostream.cpp --- a/src/hotspot/share/utilities/ostream.cpp +++ b/src/hotspot/share/utilities/ostream.cpp @@ -206,6 +206,10 @@ this->write("\n", 1); } +void outputStream::cr_indent() { + cr(); indent(); +} + void outputStream::stamp() { if (! _stamp.is_updated()) { _stamp.update(); // start at 0 on first call to stamp() @@ -303,6 +307,88 @@ } } +// Print a human readable size. +// byte_size: size, in bytes, to be printed. +// scale: one of 1 (byte-wise printing), sizeof(word) (word-size printing), K, M, G (scaled by KB, MB, GB respectively, +// or 0, which means the best scale is choosen dynamically. +// width: printing width. +void outputStream::print_human_readable_size(size_t byte_size, size_t scale, int width) { + if (scale == 0) { + // Dynamic mode. Choose scale for this value. + if (byte_size == 0) { + // Zero values are printed as bytes. + scale = 1; + } else { + if (byte_size >= G) { + scale = G; + } else if (byte_size >= M) { + scale = M; + } else if (byte_size >= K) { + scale = K; + } else { + scale = 1; + } + } + return print_human_readable_size(byte_size, scale, width); + } + +#ifdef ASSERT + assert(scale == 1 || scale == BytesPerWord || scale == K || scale == M || scale == G, "Invalid scale"); + // Special case: printing wordsize should only be done with word-sized values + if (scale == BytesPerWord) { + assert(byte_size % BytesPerWord == 0, "not word sized"); + } +#endif + + if (scale == 1) { + print("%*" PRIuPTR " bytes", width, byte_size); + } else if (scale == BytesPerWord) { + print("%*" PRIuPTR " words", width, byte_size / BytesPerWord); + } else { + const char* display_unit = ""; + switch(scale) { + case 1: display_unit = "bytes"; break; + case BytesPerWord: display_unit = "words"; break; + case K: display_unit = "KB"; break; + case M: display_unit = "MB"; break; + case G: display_unit = "GB"; break; + default: + ShouldNotReachHere(); + } + float display_value = (float) byte_size / scale; + // Since we use width to display a number with two trailing digits, increase it a bit. + width += 3; + // Prevent very small but non-null values showing up as 0.00. + if (byte_size > 0 && display_value < 0.01f) { + print("%*s %s", width, "<0.01", display_unit); + } else { + print("%*.2f %s", width, display_value, display_unit); + } + } +} + +// Prints a percentage value. Values smaller than 1% but not 0 are displayed as "<1%", values +// larger than 99% but not 100% are displayed as ">100%". +void outputStream::print_percentage(size_t total, size_t part) { + if (total == 0) { + print(" ?%%"); + } else if (part == 0) { + print(" 0%%"); + } else if (part == total) { + print("100%%"); + } else { + // Note: clearly print very-small-but-not-0% and very-large-but-not-100% percentages. + float p = ((float)part / total) * 100.0f; + if (p < 1.0f) { + print(" <1%%"); + } else if (p > 99.0f){ + print(">99%%"); + } else { + print("%3.0f%%", p); + } + } +} + stringStream::stringStream(size_t initial_size) : outputStream() { buffer_length = initial_size; buffer = NEW_RESOURCE_ARRAY(char, buffer_length); diff --git a/src/hotspot/share/utilities/ostream.hpp b/src/hotspot/share/utilities/ostream.hpp --- a/src/hotspot/share/utilities/ostream.hpp +++ b/src/hotspot/share/utilities/ostream.hpp @@ -102,8 +102,20 @@ void put(char ch); void sp(int count = 1); void cr(); + void cr_indent(); void bol() { if (_position > 0) cr(); } + // Print a human readable size. + // byte_size: size, in bytes, to be printed. + // scale: one of 1 (byte-wise printing), sizeof(word) (word-size printing), K, M, G (scaled by KB, MB, GB respectively, + // or 0, which means the best scale is choosen dynamically. + // width: printing width. + void print_human_readable_size(size_t byte_size, size_t scale = 0, int width = -1); + + // Prints a percentage value. Values smaller than 1% but not 0 are displayed as "<1%", values + // larger than 99% but not 100% are displayed as ">100%". + void print_percentage(size_t total, size_t part); + // Time stamp TimeStamp& time_stamp() { return _stamp; } void stamp(); @@ -152,7 +164,6 @@ ~streamIndentor() { _str->dec(_amount); } }; - // advisory locking for the shared tty stream: class ttyLocker: StackObj { friend class ttyUnlocker; diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -870,6 +870,13 @@ st->cr(); } + STEP("printing metaspace information") + + if (_verbose && Universe::is_fully_initialized()) { + st->print_cr("Metaspace:"); + MetaspaceUtils::print_basic_report(st, 0); + } + STEP("printing code cache information") if (_verbose && Universe::is_fully_initialized()) { @@ -1054,6 +1061,13 @@ st->cr(); } + // STEP("printing metaspace information") + + if (Universe::is_fully_initialized()) { + st->print_cr("Metaspace:"); + MetaspaceUtils::print_basic_report(st, 0); + } + // STEP("printing code cache information") if (Universe::is_fully_initialized()) { diff --git a/test/hotspot/jtreg/TEST.ROOT b/test/hotspot/jtreg/TEST.ROOT --- a/test/hotspot/jtreg/TEST.ROOT +++ b/test/hotspot/jtreg/TEST.ROOT @@ -27,7 +27,7 @@ # It also contains test-suite configuration information. # The list of keywords supported in this test suite -keys=cte_test jcmd nmt regression gc stress +keys=cte_test jcmd nmt regression gc stress metaspace groups=TEST.groups diff --git a/test/hotspot/jtreg/runtime/Metaspace/PrintMetaspaceDcmd.java b/test/hotspot/jtreg/runtime/Metaspace/PrintMetaspaceDcmd.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/Metaspace/PrintMetaspaceDcmd.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, SAP 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. + */ + +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.JDKToolFinder; + +/* + * @test + * @key metaspace jcmd + * @summary Test the VM.metaspace command + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * @run main/othervm -XX:MaxMetaspaceSize=201M -XX:+VerifyMetaspace -XX:+UseCompressedClassPointers PrintMetaspaceDcmd with-compressed-class-space + * @run main/othervm -XX:MaxMetaspaceSize=201M -XX:+VerifyMetaspace -XX:-UseCompressedClassPointers PrintMetaspaceDcmd without-compressed-class-space + */ + +public class PrintMetaspaceDcmd { + + // Run jcmd VM.metaspace against a VM with CompressedClassPointers on. + // The report should detail Non-Class and Class portions separately. + private static void doTheTest(boolean usesCompressedClassSpace) throws Exception { + ProcessBuilder pb = new ProcessBuilder(); + OutputAnalyzer output; + // Grab my own PID + String pid = Long.toString(ProcessTools.getProcessId()); + + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace", "basic"}); + output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + if (usesCompressedClassSpace) { + output.shouldContain("Non-Class:"); + output.shouldContain("Class:"); + } + output.shouldContain("Virtual space:"); + output.shouldContain("Chunk freelists:"); + + + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace"}); + output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + if (usesCompressedClassSpace) { + output.shouldContain("Non-Class:"); + output.shouldContain("Class:"); + } + output.shouldContain("Virtual space:"); + output.shouldContain("Chunk freelist"); + output.shouldContain("Waste"); + output.shouldMatch("MaxMetaspaceSize:.*201.00.*MB"); + + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace", "show-loaders"}); + output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + output.shouldMatch("ClassLoaderData.*for "); + + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace", "by-chunktype"}); + output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + output.shouldContain("specialized:"); + output.shouldContain("small:"); + output.shouldContain("medium:"); + output.shouldContain("humongous:"); + + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace", "vslist"}); + output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + output.shouldContain("Virtual space list"); + output.shouldMatch("node.*reserved.*committed.*used.*"); + + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace", "vsmap"}); + output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + output.shouldContain("Virtual space map:"); + output.shouldContain("HHHHHHHHHHH"); + + // Test with different scales + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace", "scale=G"}); + output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + output.shouldMatch("MaxMetaspaceSize:.*0.2.*GB"); + + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace", "scale=K"}); + output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + output.shouldMatch("MaxMetaspaceSize:.*205824.00 KB"); + + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace", "scale=1"}); + output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + output.shouldMatch("MaxMetaspaceSize:.*210763776 bytes"); + } + + public static void main(String args[]) throws Exception { + boolean testForCompressedClassSpace = false; + if (args[0].equals("with-compressed-class-space")) { + testForCompressedClassSpace = true; + } else if (args[0].equals("without-compressed-class-space")) { + testForCompressedClassSpace = false; + } else { + throw new IllegalArgumentException("Invalid argument: " + args[0]); + } + doTheTest(testForCompressedClassSpace); + } +} diff --git a/test/hotspot/jtreg/runtime/NMT/PrintNMTStatistics.java b/test/hotspot/jtreg/runtime/NMT/PrintNMTStatistics.java --- a/test/hotspot/jtreg/runtime/NMT/PrintNMTStatistics.java +++ b/test/hotspot/jtreg/runtime/NMT/PrintNMTStatistics.java @@ -46,6 +46,10 @@ OutputAnalyzer output_detail = new OutputAnalyzer(pb.start()); output_detail.shouldContain("Virtual memory map:"); output_detail.shouldContain("Details:"); + + // PrintNMTStatistics also prints out metaspace statistics as a convenience. + output_detail.shouldContain("Metaspace:"); + output_detail.shouldHaveExitValue(0); // Make sure memory reserved for Module processing is recorded.