< prev index next >

src/hotspot/share/memory/metaspace.cpp

Print this page
rev 49736 : [mq]: metaspace-additions-to-jcmd

*** 41,50 **** --- 41,51 ---- #include "runtime/atomic.hpp" #include "runtime/globals.hpp" #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" #include "utilities/align.hpp" #include "utilities/copy.hpp"
*** 68,77 **** --- 69,100 ---- size_t Metaspace::_compressed_class_space_size; const MetaspaceTracer* Metaspace::_tracer = NULL; DEBUG_ONLY(bool Metaspace::_frozen = false;) + // Internal statistics. + #ifdef ASSERT + struct { + // Number of allocations (from outside) + uintx num_allocs; + // Number of times a ClassLoaderMetaspace was born. + uintx num_metaspace_births; + // Number of times a ClassLoaderMetaspace 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 (e.g. retransformClasses etc) + uintx num_deallocs; + // Number of times an alloc was satisfied from deallocated blocks. + uintx num_allocs_from_deallocated_blocks; + } g_internal_statistics; + #endif + enum ChunkSizes { // in words. ClassSpecializedChunk = 128, SpecializedChunk = 128, ClassSmallChunk = 256, SmallChunk = 512,
*** 142,167 **** static 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; } } volatile intptr_t MetaspaceGC::_capacity_until_GC = 0; uint MetaspaceGC::_shrink_factor = 0; bool MetaspaceGC::_should_concurrent_collect = false; typedef class FreeList<Metachunk> ChunkList; // Manages the global free lists of chunks. class ChunkManager : public CHeapObj<mtInternal> { friend class TestVirtualSpaceNodeTest; --- 165,573 ---- static ChunkIndex prev_chunk_index(ChunkIndex i) { assert(i > ZeroIndex, "Out of bound"); return (ChunkIndex) (i-1); } + 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; + } + assert(s != NULL, "Invalid space type"); + return s; + } + static const char* scale_unit(size_t scale) { switch(scale) { ! case 1: return "bytes"; ! case sizeof(MetaWord): return "words"; case K: return "KB"; case M: return "MB"; case G: return "GB"; default: ShouldNotReachHere(); return NULL; } } + // Print a size, in bytes, scaled. + static void print_scaled_bytes(outputStream* st, size_t byte_size, size_t scale = 0, int width = -1) { + 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_scaled_bytes(st, byte_size, scale, width); + } + + #ifdef ASSERT + assert(scale == 1 || scale == sizeof(MetaWord) || scale == K || scale == M || scale == G, "Invalid scale"); + // Special case: printing wordsize should only be done with word-sized values + if (scale == sizeof(MetaWord)) { + assert(byte_size % sizeof(MetaWord) == 0, "not word sized"); + } + #endif + + if (scale == 1) { + st->print("%*" PRIuPTR " bytes", width, byte_size); + } else if (scale == sizeof(MetaWord)) { + st->print("%*" PRIuPTR " words", width, byte_size / sizeof(MetaWord)); + } else { + const char* const unit = scale_unit(scale); + 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) { + st->print("%*s %s", width, "<0.01", unit); + } else { + st->print("%*.2f %s", width, display_value, unit); + } + } + } + + // Print a size, in words, scaled. + static void print_scaled_words(outputStream* st, size_t word_size, size_t scale = 0, int width = -1) { + print_scaled_bytes(st, word_size * sizeof(MetaWord), scale, width); + } + + static void print_percentage(outputStream* st, size_t total, size_t part) { + if (total == 0) { + st->print(" ?%%"); + } else if (part == 0) { + st->print(" 0%%"); + } else { + float p = ((float)part / total) * 100.0f; + if (p < 1.0f) { + st->print(" <1%%"); + } else { + st->print("%3.0f%%", p); + } + } + } + + // Convenience helper: prints a size value and a percentage. + static void print_scaled_words_and_percentage(outputStream* st, size_t word_size, size_t compare_word_size, size_t scale = 0, int width = -1) { + print_scaled_words(st, word_size, scale, width); + st->print(" ("); + print_percentage(st, compare_word_size, word_size); + st->print(")"); + } + volatile intptr_t MetaspaceGC::_capacity_until_GC = 0; uint MetaspaceGC::_shrink_factor = 0; bool MetaspaceGC::_should_concurrent_collect = false; + /// statistics /////// + + // Contains statistics for a number of free chunks. + class FreeChunksStatistics { + uintx _num; // Number of chunks + size_t _cap; // Total capacity, in words + + public: + FreeChunksStatistics() : _num(0), _cap(0) {} + + void reset() { + _num = 0; _cap = 0; + } + + uintx num() const { return _num; } + size_t cap() const { return _cap; } + + void add(uintx n, size_t s) { _num += n; _cap += s; } + void add(const FreeChunksStatistics& other) { + _num += other._num; + _cap += other._cap; + } + + void print_on(outputStream* st, size_t scale) const { + st->print(UINTX_FORMAT, _num); + st->print(" chunks, total capacity "); + print_scaled_words(st, _cap, scale); + } + + }; // 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() { + for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { + _chunk_stats[i].reset(); + } + } + + size_t total_capacity() const { + return _chunk_stats[SpecializedIndex].cap() + + _chunk_stats[SmallIndex].cap() + + _chunk_stats[MediumIndex].cap() + + _chunk_stats[HumongousIndex].cap(); + } + + void 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(); + } + + }; // 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 + + public: + + UsedChunksStatistics() + : _num(0), _cap(0), _used(0), _free(0), _waste(0) + {} + + void reset() { + _num = 0; + _cap = _used = _free = _waste = 0; + } + + 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; } + + 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(const UsedChunksStatistics& other) { + _num += other._num; + _cap += other._cap; + _used += other._used; + _free += other._free; + _waste += other._waste; + } + + void print_on(outputStream* st, size_t scale) const { + int col = st->position(); + st->print(UINTX_FORMAT_W(3) " chunk%s, ", _num, _num != 1 ? "s" : ""); + if (_num > 0) { + col += 12; 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"); + } + } + + }; // 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() { reset(); } + + void reset() { + for (int i = 0; i < NumberOfInUseLists; i ++) { + _chunk_stats[i].reset(); + _free_blocks_num = 0; _free_blocks_cap_words = 0; + } + } + + void add_free_blocks(uintx num, size_t cap) { + _free_blocks_num += num; + _free_blocks_cap_words += cap; + } + + // 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; } + + // Returns total chunk statistics over all chunk types. + UsedChunksStatistics totals() const { + UsedChunksStatistics stat; + for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { + stat.add(_chunk_stats[i]); + } + return stat; + } + + void 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; + } + + void print_on(outputStream* st, size_t scale, bool detailed) const { + UsedChunksStatistics totals; + for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { + totals.add(_chunk_stats[i]); + } + streamIndentor sti(st); + if (detailed) { + st->cr_indent(); + } + totals.print_on(st, scale); + if (_free_blocks_num > 0) { + if (detailed) { + st->cr_indent(); + } else { + st->print(", "); + } + st->print("deallocated: " UINTX_FORMAT " blocks with ", _free_blocks_num); + print_scaled_words(st, _free_blocks_cap_words, scale); + } + if (detailed) { + st->cr_indent(); + st->print("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); + } + } + } + } + } + + }; // SpaceManagerStatistics + + class ClassLoaderMetaspaceStatistics { + + SpaceManagerStatistics _sm_stats[Metaspace::MetadataTypeCount]; + + public: + + ClassLoaderMetaspaceStatistics() { reset(); } + + void reset() { + nonclass_sm_stats().reset(); + if (Metaspace::using_class_space()) { + class_sm_stats().reset(); + } + } + + 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); } + + // Returns total space manager statistics for both class and non-class metaspace + SpaceManagerStatistics totals() const { + SpaceManagerStatistics stats; + stats.add(nonclass_sm_stats()); + if (Metaspace::using_class_space()) { + stats.add(class_sm_stats()); + } + return stats; + } + + void 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 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 (Metaspace::using_class_space()) { + st->cr_indent(); + st->print("Class: "); + class_sm_stats().print_on(st, scale, detailed); + } + st->cr(); + } + + }; // ClassLoaderMetaspaceStatistics + + typedef class FreeList<Metachunk> ChunkList; // Manages the global free lists of chunks. class ChunkManager : public CHeapObj<mtInternal> { friend class TestVirtualSpaceNodeTest;
*** 238,260 **** // Note that this chunk is supposed to be removed from the freelist right away. Metachunk* split_chunk(size_t target_chunk_word_size, Metachunk* chunk); 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)); _free_chunks[SmallIndex].set_size(get_size_for_nonhumongous_chunktype(SmallIndex, is_class)); _free_chunks[MediumIndex].set_size(get_size_for_nonhumongous_chunktype(MediumIndex, is_class)); --- 644,653 ----
*** 356,368 **** void locked_print_free_chunks(outputStream* st); void locked_print_sum_free_chunks(outputStream* st); void print_on(outputStream* st) const; ! // Prints composition for both non-class and (if available) ! // class chunk manager. ! static void print_all_chunkmanagers(outputStream* out, size_t scale = 1); }; class SmallBlocks : public CHeapObj<mtClass> { const static uint _small_block_max_size = sizeof(TreeChunk<Metablock, FreeList<Metablock> >)/HeapWordSize; const static uint _small_block_min_size = sizeof(Metablock)/HeapWordSize; --- 749,760 ---- void locked_print_free_chunks(outputStream* st); void locked_print_sum_free_chunks(outputStream* st); void print_on(outputStream* st) const; ! void get_statistics(ChunkManagerStatistics* out) const; ! }; class SmallBlocks : public CHeapObj<mtClass> { const static uint _small_block_max_size = sizeof(TreeChunk<Metablock, FreeList<Metablock> >)/HeapWordSize; const static uint _small_block_min_size = sizeof(Metablock)/HeapWordSize;
*** 390,399 **** --- 782,800 ---- result = result + _small_lists[k].count() * _small_lists[k].size(); } return result; } + 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; } MetaWord* get_block(size_t word_size) { if (list_at(word_size).count() > 0) {
*** 450,459 **** --- 851,868 ---- result = result + _small_blocks->total_size(); } return result; } + 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<Metablock, FreeList<Metablock> >::min_size(); } void print_on(outputStream* st) const; }; // Helper for Occupancy Bitmap. A type trait to give an all-bits-are-one-unsigned constant.
*** 856,865 **** --- 1265,1275 ---- // the smallest chunk size. void retire(ChunkManager* chunk_manager); void print_on(outputStream* st) const; + void print_on(outputStream* st, size_t scale) const; void print_map(outputStream* st, bool is_class) const; // Debug support DEBUG_ONLY(void mangle();) // Verify counters, all chunks in this list node and the occupancy map.
*** 873,882 **** --- 1283,1298 ---- #define assert_is_aligned(value, alignment) \ assert(is_aligned((value), (alignment)), \ 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()) { size_t words = bytes / BytesPerWord; bool is_class = false; // We never reserve large pages for the class space.
*** 1180,1189 **** --- 1596,1606 ---- // Unlink empty VirtualSpaceNodes and free it. void purge(ChunkManager* chunk_manager); void print_on(outputStream* st) const; + void print_on(outputStream* st, size_t scale) const; void print_map(outputStream* st) const; class VirtualSpaceListIterator : public StackObj { VirtualSpaceNode* _virtual_spaces; public:
*** 1216,1225 **** --- 1633,1643 ---- #endif }; int Metadebug::_allocation_fail_alot_count = 0; + // SpaceManager - used by Metaspace to handle allocations class SpaceManager : public CHeapObj<mtClass> { friend class ClassLoaderMetaspace; friend class Metadebug;
*** 1245,1256 **** // Maximum number of specialize chunks to allocate for anonymous and delegating // 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; --- 1663,1674 ---- // Maximum number of specialize chunks to allocate for anonymous and delegating // metadata space to a SpaceManager static uint const _anon_and_delegating_metadata_specialize_chunk_limit; ! // Sum of used space in chunks, including overhead incurred by chunk headers. ! size_t _allocated_block_words; // Sum of all allocated chunks size_t _allocated_chunks_words; size_t _allocated_chunks_count;
*** 1285,1294 **** --- 1703,1718 ---- void add_chunk(Metachunk* v, bool make_current); void retire_current_chunk(); Mutex* lock() const { return _lock; } + // Adds to the given statistic object. Must be locked with CLD metaspace lock. + void add_to_statistics_locked(SpaceManagerStatistics* out) const; + + // Verify internal counters against the current state. Must be locked with CLD metaspace lock. + DEBUG_ONLY(void verify_metrics_locked() const;) + protected: void initialize(); public: SpaceManager(Metaspace::MetadataType mdtype,
*** 1315,1326 **** size_t smallest_chunk_size() const { return smallest_chunk_size(is_class()); } 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(); } --- 1739,1750 ---- size_t smallest_chunk_size() const { return smallest_chunk_size(is_class()); } size_t medium_chunk_bunch() const { return medium_chunk_size() * MediumChunkMultiple; } ! size_t allocated_blocks_words() const { return _allocated_block_words; } ! size_t allocated_blocks_bytes() const { return _allocated_block_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(); }
*** 1343,1359 **** size_t adjust_initial_chunk_size(size_t requested) const; // 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(); size_t sum_count_in_chunks_in_use(ChunkIndex i); Metachunk* get_new_chunk(size_t chunk_word_size); // Block allocation and deallocation. --- 1767,1777 ---- size_t adjust_initial_chunk_size(size_t requested) const; // Get the initial chunks size for this metaspace type. size_t get_initial_chunk_size(Metaspace::MetaspaceType type) const; ! // Todo: remove this if 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); // Block allocation and deallocation.
*** 1378,1396 **** // Notify memory usage to MemoryService. void track_metaspace_memory_usage(); // 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. size_t get_allocation_word_size(size_t word_size) { size_t byte_size = word_size * BytesPerWord; --- 1796,1810 ----
*** 1401,1410 **** --- 1815,1831 ---- size_t raw_word_size = raw_bytes_size / BytesPerWord; assert(raw_word_size * BytesPerWord == raw_bytes_size, "Size problem"); return raw_word_size; } + + // Adds to the given statistic object. Will lock with CLD metaspace lock. + void add_to_statistics(SpaceManagerStatistics* out) const; + + // Verify internal counters against the current state. Will lock with CLD metaspace lock. + DEBUG_ONLY(void verify_metrics() const;) + }; uint const SpaceManager::_small_chunk_limit = 4; uint const SpaceManager::_anon_and_delegating_metadata_specialize_chunk_limit = 4;
*** 1701,1710 **** --- 2122,2132 ---- bool result = virtual_space()->expand_by(commit, false); 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); }
*** 1761,1778 **** return result; } void VirtualSpaceNode::print_on(outputStream* st) const { ! size_t used = used_words_in_vs(); ! size_t capacity = capacity_words_in_vs(); VirtualSpace* vs = virtual_space(); ! st->print_cr(" space @ " PTR_FORMAT " " SIZE_FORMAT "K, " SIZE_FORMAT_W(3) "%% used " ! "[" 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())); } #ifdef ASSERT --- 2183,2211 ---- return result; } void VirtualSpaceNode::print_on(outputStream* st) const { ! print_on(st, K); ! } ! ! 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("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(bottom()), p2i(top()), p2i(end()), p2i(vs->high_boundary())); } #ifdef ASSERT
*** 1990,1999 **** --- 2423,2433 ---- // Don't free the current virtual space since it will likely // be needed soon. 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. assert(vsl == virtual_space_list(), "Expected to be the first node"); set_virtual_space_list(vsl->next());
*** 2137,2146 **** --- 2571,2581 ---- assert(new_entry->reserved_words() == vs_word_size, "Reserved memory size differs from requested memory size"); // 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; } } void VirtualSpaceList::link_vs(VirtualSpaceNode* new_entry) {
*** 2286,2299 **** return next; } void VirtualSpaceList::print_on(outputStream* st) const { VirtualSpaceListIterator iter(virtual_space_list()); while (iter.repeat()) { VirtualSpaceNode* node = iter.get_next(); ! node->print_on(st); } } void VirtualSpaceList::print_map(outputStream* st) const { VirtualSpaceNode* list = virtual_space_list(); --- 2721,2741 ---- return next; } void VirtualSpaceList::print_on(outputStream* st) const { + print_on(st, K); + } + + 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, scale); } } void VirtualSpaceList::print_map(outputStream* st) const { VirtualSpaceNode* list = virtual_space_list();
*** 3070,3153 **** void ChunkManager::print_on(outputStream* out) const { _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."); } } // SpaceManager methods --- 3512,3525 ---- void ChunkManager::print_on(outputStream* out) const { _humongous_dictionary.report_statistics(out); } ! void ChunkManager::get_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)); } } // SpaceManager methods
*** 3199,3303 **** SIZE_FORMAT " adjusted: " SIZE_FORMAT, requested, adjusted); 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); while (chunk != NULL) { count++; chunk = chunk->next(); } 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)) { Metachunk* chunk = chunks_in_use(i); st->print("SpaceManager: %s " PTR_FORMAT, --- 3571,3590 ----
*** 3415,3450 **** return mem; } 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()); ! } } SpaceManager::SpaceManager(Metaspace::MetadataType mdtype, Metaspace::MetaspaceType space_type, Mutex* lock) : _mdtype(mdtype), _space_type(space_type), ! _allocated_blocks_words(0), _allocated_chunks_words(0), _allocated_chunks_count(0), _block_freelists(NULL), _lock(lock) { --- 3702,3722 ---- return mem; } void SpaceManager::print_on(outputStream* st) const { ! SpaceManagerStatistics stat; ! add_to_statistics(&stat); // will lock _lock. ! stat.print_on(st, 1*K, false); } SpaceManager::SpaceManager(Metaspace::MetadataType mdtype, Metaspace::MetaspaceType space_type, Mutex* lock) : _mdtype(mdtype), _space_type(space_type), ! _allocated_block_words(0), _allocated_chunks_words(0), _allocated_chunks_count(0), _block_freelists(NULL), _lock(lock) {
*** 3455,3464 **** --- 3727,3737 ---- 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
*** 3467,3486 **** MetaspaceUtils::inc_used(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 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::initialize() { Metadebug::init_allocation_fail_alot_count(); for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { --- 3740,3757 ---- MetaspaceUtils::inc_used(mdtype(), Metachunk::overhead()); } void SpaceManager::inc_used_metrics(size_t words) { // Add to the per SpaceManager total ! Atomic::add(words, &_allocated_block_words); // Add to the global total 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()); } void SpaceManager::initialize() { Metadebug::init_allocation_fail_alot_count(); for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) {
*** 3489,3512 **** _current_chunk = NULL; log_trace(gc, metaspace, freelist)("SpaceManager(): " PTR_FORMAT, p2i(this)); } 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()); 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(); Log(gc, metaspace, freelist) log; --- 3760,3776 ---- _current_chunk = NULL; log_trace(gc, metaspace, freelist)("SpaceManager(): " PTR_FORMAT, p2i(this)); } SpaceManager::~SpaceManager() { + // This call this->_lock which can't be done while holding MetaspaceExpand_lock ! DEBUG_ONLY(verify_metrics()); MutexLockerEx fcl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); chunk_manager()->slow_locked_verify(); dec_total_from_size_metrics(); Log(gc, metaspace, freelist) log;
*** 3538,3548 **** delete _block_freelists; } } void SpaceManager::deallocate(MetaWord* p, size_t word_size) { ! 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 if (block_freelists() == NULL) { _block_freelists = new BlockFreelist(); --- 3802,3812 ---- delete _block_freelists; } } void SpaceManager::deallocate(MetaWord* p, size_t word_size) { ! 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 if (block_freelists() == NULL) { _block_freelists = new BlockFreelist();
*** 3589,3599 **** inc_size_metrics(new_chunk->word_size()); 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()); ResourceMark rm; LogStream ls(log.trace()); new_chunk->print_on(&ls); chunk_manager()->locked_print_free_chunks(&ls); } --- 3853,3863 ---- inc_size_metrics(new_chunk->word_size()); 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 ") ", _allocated_chunks_count); ResourceMark rm; LogStream ls(log.trace()); new_chunk->print_on(&ls); chunk_manager()->locked_print_free_chunks(&ls); }
*** 3638,3647 **** --- 3902,3914 ---- // from the dictionary until it starts to get fat. Is this // a reasonable policy? Maybe an skinny dictionary is fast enough // 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); }
*** 3649,3659 **** } // 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); #ifdef ASSERT if (Metadebug::test_metadata_failure()) { return NULL; } #endif --- 3916,3926 ---- } // 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()); #ifdef ASSERT if (Metadebug::test_metadata_failure()) { return NULL; } #endif
*** 3695,3759 **** chunk->word_size() == specialized_chunk_size(), "Chunk size is wrong"); return; } ! #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()); } ! #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); } // MetaspaceUtils size_t MetaspaceUtils::_capacity_words[] = {0, 0}; volatile size_t MetaspaceUtils::_used_words[] = {0, 0}; size_t MetaspaceUtils::free_bytes(Metaspace::MetadataType mdtype) { VirtualSpaceList* list = Metaspace::get_space_list(mdtype); return list == NULL ? 0 : list->free_bytes(); } --- 3962,4040 ---- chunk->word_size() == specialized_chunk_size(), "Chunk size is wrong"); 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_used(chunk->used_word_size()); ! 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(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_metrics_locked() const { ! assert_lock_strong(lock()); ! SpaceManagerStatistics stat; ! add_to_statistics_locked(&stat); ! ! UsedChunksStatistics chunk_stats = stat.totals(); ! assert_counter(_allocated_block_words, chunk_stats.used(), "SpaceManager::_allocated_blocks_words"); ! assert_counter(_allocated_chunks_words, chunk_stats.cap(), "SpaceManager::_allocated_chunks_words"); ! assert_counter(_allocated_chunks_count, chunk_stats.num(), "SpaceManager::_allocated_chunks_count"); ! } ! 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}; + + // 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_bytes(Metaspace::MetadataType mdtype) { VirtualSpaceList* list = Metaspace::get_space_list(mdtype); return list == NULL ? 0 : list->free_bytes(); }
*** 3765,3774 **** --- 4046,4056 ---- 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; } void MetaspaceUtils::inc_capacity(Metaspace::MetadataType mdtype, size_t words) { assert_lock_strong(MetaspaceExpand_lock);
*** 3794,3862 **** // generally done concurrently by different application // threads so must be done atomically. Atomic::add(words, &_used_words[mdtype]); } - 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; - } - - 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; - } - size_t MetaspaceUtils::reserved_bytes(Metaspace::MetadataType mdtype) { VirtualSpaceList* list = Metaspace::get_space_list(mdtype); return list == NULL ? 0 : list->reserved_bytes(); } --- 4076,4085 ----
*** 3932,4215 **** committed_bytes(ct)/K, reserved_bytes(ct)/K); } } ! // 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; public: ! PrintCLDMetaspaceInfoClosure(outputStream* out, size_t scale = K) ! : _out(out), _scale(scale), _total_count(0), _total_anon_count(0) { } ! ! ~PrintCLDMetaspaceInfoClosure() { ! print_summary(); } void do_cld(ClassLoaderData* cld) { assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint"); - if (cld->is_unloading()) return; 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()); } ! print_metaspace(msp, anonymous); ! _out->cr(); } ! private: ! void print_metaspace(ClassLoaderMetaspace* msp, bool anonymous); ! void print_summary() const; ! }; ! ! 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); ! if (Metaspace::using_class_space()) { ! vsm = msp->class_vsm(); ! capacity = vsm->sum_capacity_in_chunks_in_use() * BytesPerWord; ! used = vsm->sum_used_in_chunks_in_use() * BytesPerWord; ! free = vsm->sum_free_in_chunks_in_use() * BytesPerWord; ! waste = vsm->sum_waste_in_chunks_in_use() * BytesPerWord; ! MetadataStats class_stats(capacity, used, free, waste); ! _total_class.add(class_stats); ! if (anonymous) { ! _total_anon_class.add(class_stats); } ! _out->print(" Class data "); ! class_stats.print_on(_out, _scale); } - } ! void PrintCLDMetaspaceInfoClosure::print_summary() const { ! const char* unit = scale_unit(_scale); ! _out->cr(); ! _out->print_cr("Summary:"); ! MetadataStats total; ! total.add(_total_metadata); ! total.add(_total_class); ! _out->print(" Total class loaders=" SIZE_FORMAT_W(6) " ", _total_count); ! total.print_on(_out, _scale); ! _out->print(" Metadata "); ! _total_metadata.print_on(_out, _scale); if (Metaspace::using_class_space()) { ! _out->print(" Class data "); ! _total_class.print_on(_out, _scale); } ! _out->cr(); ! MetadataStats total_anon; ! total_anon.add(_total_anon_metadata); ! total_anon.add(_total_anon_class); ! _out->print("For anonymous classes=" SIZE_FORMAT_W(6) " ", _total_anon_count); ! total_anon.print_on(_out, _scale); ! _out->print(" Metadata "); ! _total_anon_metadata.print_on(_out, _scale); if (Metaspace::using_class_space()) { ! _out->print(" Class data "); ! _total_anon_class.print_on(_out, _scale); } ! } - void 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); if (Metaspace::using_class_space()) { ! out->print_cr(" Class space: reserved=" SIZE_FORMAT_W(10) "%s committed=" SIZE_FORMAT_W(10) "%s", ! reserved_bytes(Metaspace::ClassType) / scale, unit, ! committed_bytes(Metaspace::ClassType) / scale, unit); } out->cr(); ! ChunkManager::print_all_chunkmanagers(out, scale); out->cr(); ! out->print_cr("Per-classloader metadata:"); out->cr(); ! PrintCLDMetaspaceInfoClosure cl(out, scale); ! ClassLoaderDataGraph::cld_do(&cl); ! } ! // Dump global metaspace things from the end of ClassLoaderDataGraph ! void 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); ! } // Prints an ASCII representation of the given space. void MetaspaceUtils::print_metaspace_map(outputStream* out, Metaspace::MetadataType mdtype) { MutexLockerEx cl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); const bool for_class = mdtype == Metaspace::ClassType ? true : false; --- 4155,4517 ---- committed_bytes(ct)/K, reserved_bytes(ct)/K); } } ! class PrintCLDMetaspaceInfoClosure : public CLDClosure { private: ! outputStream* const _out; ! const size_t _scale; ! const bool _do_print; ! const bool _break_down_by_chunktype; public: ! uintx _num_loaders_with_metaspace; ! uintx _num_loaders_without_metaspace; ! 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_with_metaspace(0) ! , _num_loaders_without_metaspace(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"); ClassLoaderMetaspace* msp = cld->metaspace_or_null(); if (msp == NULL) { + _num_loaders_without_metaspace ++; return; } ! // 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_with_metaspace ++; ! _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_with_metaspace); ! 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 (msp->space_type() != Metaspace::StandardMetaspaceType) { ! _out->print(", %s loader", space_type_name(msp->space_type())); } ! if (cld->is_unloading()) { ! _out->print(", unloading"); } ! _out->cr(); ! this_cld_stat.print_on(_out, _scale, _break_down_by_chunktype); ! _out->cr(); ! } ! ! } // do_cld ! }; ! 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; ! bool have_detailed_cl_data = false; ! ! // 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(); ! } ! if (print_loaders || print_by_chunktype || print_by_spacetype) { ! ClassLoaderDataGraph::cld_do(&cl); // collect data and optionally print ! have_detailed_cl_data = true; } ! // 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 " spaces%c", ! space_type_name((Metaspace::MetaspaceType)space_type), ! num, num > 0 ? ':' : '.'); ! if (num > 0) { ! out->cr(); ! cl._stats_by_spacetype[space_type].print_on(out, scale, print_by_chunktype); ! } ! out->cr(); ! } } ! // Print totals for in-use data: ! out->cr(); ! out->print_cr("Total Usage:"); ! out->cr(); ! if (have_detailed_cl_data) { ! out->print_cr(UINTX_FORMAT " loaders (" UINTX_FORMAT " without metaspace)", ! cl._num_loaders_with_metaspace + cl._num_loaders_without_metaspace, cl._num_loaders_without_metaspace); ! out->cr(); ! cl._stats_total.print_on(out, scale, print_by_chunktype); ! } else { ! // In its most basic form, we do not require walking the CLDG. Instead, just print the running totals from ! // MetaspaceUtils. ! const size_t cap_nonclass = MetaspaceUtils::capacity_words(Metaspace::NonClassType); ! const size_t used_nonclass = MetaspaceUtils::used_words(Metaspace::NonClassType); ! const size_t free_and_waste_nonclass = cap_nonclass - used_nonclass; ! if (Metaspace::using_class_space()) { ! out->print_cr(" Non-class space:"); ! } ! print_scaled_words(out, cap_nonclass, scale, 6); ! out->print(" capacity, "); ! print_scaled_words_and_percentage(out, used_nonclass, cap_nonclass, scale, 6); ! out->print(" used, "); ! print_scaled_words_and_percentage(out, free_and_waste_nonclass, cap_nonclass, scale, 6); ! out->print(" free+waste. "); ! if (Metaspace::using_class_space()) { ! out->print_cr(" Class space:"); ! const size_t cap_class = MetaspaceUtils::capacity_words(Metaspace::ClassType); ! const size_t used_class = MetaspaceUtils::used_words(Metaspace::ClassType); ! const size_t free_and_waste_class = cap_class - used_class; ! print_scaled_words(out, cap_class, scale, 6); ! out->print(" capacity, "); ! print_scaled_words_and_percentage(out, used_class, cap_class, scale, 6); ! out->print(" used, "); ! print_scaled_words_and_percentage(out, free_and_waste_class, cap_class, scale, 6); ! out->print(" free+waste. "); ! ! out->print_cr(" Total:"); ! const size_t cap = cap_nonclass + cap_class; ! const size_t used = used_nonclass + used_class; ! const size_t free_and_waste = free_and_waste_nonclass + free_and_waste_class; ! print_scaled_words(out, cap, scale, 6); ! out->print(" capacity, "); ! print_scaled_words_and_percentage(out, used, cap, scale, 6); ! out->print(" used, "); ! print_scaled_words_and_percentage(out, free_and_waste, cap, scale, 6); ! out->print(" free+waste. "); ! } ! out->cr(); ! } ! // -- Print Virtual space. ! out->cr(); ! out->print_cr("Virtual Space:"); ! out->cr(); ! const size_t reserved_nonclass_words = reserved_bytes(Metaspace::NonClassType) / sizeof(MetaWord); ! const size_t committed_nonclass_words = committed_bytes(Metaspace::NonClassType) / sizeof(MetaWord); ! const size_t reserved_class_words = reserved_bytes(Metaspace::ClassType) / sizeof(MetaWord); ! const size_t committed_class_words = committed_bytes(Metaspace::ClassType) / sizeof(MetaWord); ! const size_t reserved_words = reserved_nonclass_words + reserved_class_words; ! const size_t committed_words = committed_nonclass_words + committed_class_words; ! { ! if (Metaspace::using_class_space()) { ! out->print_cr(" 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(" committed "); if (Metaspace::using_class_space()) { ! out->print_cr(" Class space:"); ! print_scaled_words(out, reserved_nonclass_words, scale, 7); ! out->print(" reserved, "); ! print_scaled_words_and_percentage(out, committed_class_words, reserved_class_words, scale, 7); ! out->print(" committed "); ! ! out->print_cr(" Total:"); ! 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(" committed "); } ! } ! out->cr(); ! // -- Print VirtualSpaceList details. ! if ((flags & rf_show_vslist) > 0) { ! out->cr(); ! out->print_cr("Virtual Space List%s:", Metaspace::using_class_space() ? "s" : ""); ! out->cr(); ! 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:"); ! out->cr(); ! 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("Free Chunk List%s:", Metaspace::using_class_space() ? "s" : ""); ! out->cr(); ! if ((flags & rf_show_chunk_freelist) > 0) { ! ChunkManagerStatistics non_class_cm_stat; ! Metaspace::chunk_manager_metadata()->get_statistics(&non_class_cm_stat); ! ChunkManagerStatistics class_cm_stat; ! Metaspace::chunk_manager_class()->get_statistics(&class_cm_stat); if (Metaspace::using_class_space()) { ! out->print_cr(" Non-Class:"); } ! non_class_cm_stat.print_on(out, scale); if (Metaspace::using_class_space()) { ! out->print_cr(" Class:"); ! class_cm_stat.print_on(out, scale); ! } ! } else { ! // In its basic form, report only capacity in free chunks, but take those numbers from the ! // running totals in the chunk managers to avoid locking. ! if (Metaspace::using_class_space()) { ! out->print_cr(" Non-Class:"); ! } ! print_scaled_bytes(out, Metaspace::chunk_manager_metadata()->free_chunks_total_words(), scale); ! out->cr(); ! if (Metaspace::using_class_space()) { ! out->print_cr(" Class:"); ! print_scaled_bytes(out, Metaspace::chunk_manager_class()->free_chunks_total_words(), scale); ! out->cr(); ! } ! out->cr(); } + // As a convenience, print a summary of common waste. + out->cr(); + out->print_cr("Waste:"); + // For all wastages, print percentages from total. As total use the total size of memory committed for metaspace. + out->print(" (Percentages are of total committed metaspace size ("); + print_scaled_words(out, committed_words, scale); + out->print_cr(")"); + + // Print waste for in-use chunks. + if (have_detailed_cl_data) { + 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(); ! } else { ! // if we did not walk the CLDG, use the running numbers. ! size_t free_and_waste_words = MetaspaceUtils::capacity_words() - MetaspaceUtils::used_words(); ! out->print("Free+Waste in chunks in use: "); ! print_scaled_words_and_percentage(out, free_and_waste_words, committed_words, scale, 6); ! out->cr(); ! } + // Print waste in deallocated blocks. + if (have_detailed_cl_data) { + 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 waste in free chunks. ! { ! const size_t total_capacity_in_free_chunks = ! Metaspace::chunk_manager_metadata()->free_chunks_total_words() + ! Metaspace::chunk_manager_class()->free_chunks_total_words(); ! out->print("In free chunks: "); ! print_scaled_words_and_percentage(out, total_capacity_in_free_chunks, 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 de-allocations: " UINTX_FORMAT ".", g_internal_statistics.num_deallocs); ! out->print_cr("Allocs statisfied 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: "); + print_scaled_bytes(out, MaxMetaspaceSize, scale); + out->cr(); + out->print("UseCompressedClassPointers: %s", UseCompressedClassPointers ? "true" : "false"); + out->cr(); + out->print("CompressedClassSpaceSize: "); + print_scaled_bytes(out, CompressedClassSpaceSize, scale); ! out->cr(); ! out->cr(); ! ! } // MetaspaceUtils::print_report() // Prints an ASCII representation of the given space. void MetaspaceUtils::print_metaspace_map(outputStream* out, Metaspace::MetadataType mdtype) { MutexLockerEx cl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); const bool for_class = mdtype == Metaspace::ClassType ? true : false;
*** 4239,4295 **** if (Metaspace::using_class_space()) { Metaspace::chunk_manager_class()->verify(); } } ! void MetaspaceUtils::verify_capacity() { ! #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); ! } ! #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 size_t Metaspace::_first_chunk_word_size = 0; size_t Metaspace::_first_class_chunk_word_size = 0; --- 4541,4583 ---- if (Metaspace::using_class_space()) { Metaspace::chunk_manager_class()->verify(); } } ! void MetaspaceUtils::verify_metrics() { #ifdef ASSERT ! // 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 = ! _capacity_words[Metaspace::NonClassType] != nonclass_chunk_stat.cap() || ! _used_words[Metaspace::NonClassType] != nonclass_chunk_stat.used() || ! _capacity_words[Metaspace::ClassType] != class_chunk_stat.cap() || ! _used_words[Metaspace::ClassType] != class_chunk_stat.used(); ! if (mismatch) { ! tty->print_cr("MetaspaceUtils::verify_metrics: counter mismatch."); ! tty->print_cr("Expected: non-class cap: " SIZE_FORMAT ", non-class used: " SIZE_FORMAT ! ", class cap: " SIZE_FORMAT ", class used: " SIZE_FORMAT ".", ! _capacity_words[Metaspace::NonClassType], _used_words[Metaspace::NonClassType], ! _capacity_words[Metaspace::ClassType], _used_words[Metaspace::ClassType]); ! tty->print_cr("Got: non-class: "); ! nonclass_chunk_stat.print_on(tty, sizeof(MetaWord)); ! tty->cr(); ! tty->print_cr(" class: "); ! class_chunk_stat.print_on(tty, sizeof(MetaWord)); ! tty->cr(); ! tty->flush(); } + assert(mismatch == false, "MetaspaceUtils::verify_metrics: counter mismatch."); #endif } // Metaspace methods size_t Metaspace::_first_chunk_word_size = 0; size_t Metaspace::_first_class_chunk_word_size = 0;
*** 4708,4720 **** LogStream ls(log.debug()); loader_data->print_value_on(&ls); } } LogStream ls(log.info()); ! MetaspaceUtils::dump(&ls); ! MetaspaceUtils::print_metaspace_map(&ls, mdtype); ! ChunkManager::print_all_chunkmanagers(&ls); } bool out_of_compressed_class_space = false; if (is_class_space_allocation(mdtype)) { ClassLoaderMetaspace* metaspace = loader_data->metaspace_non_null(); --- 4996,5007 ---- LogStream ls(log.debug()); loader_data->print_value_on(&ls); } } LogStream ls(log.info()); ! // In case of an OOM, log out a short but still useful report. ! MetaspaceUtils::print_report(&ls); } bool out_of_compressed_class_space = false; if (is_class_space_allocation(mdtype)) { ClassLoaderMetaspace* metaspace = loader_data->metaspace_non_null();
*** 4785,4804 **** return get_space_list(NonClassType)->contains(ptr); } // ClassLoaderMetaspace ! ClassLoaderMetaspace::ClassLoaderMetaspace(Mutex* lock, Metaspace::MetaspaceType type) { initialize(lock, type); } ClassLoaderMetaspace::~ClassLoaderMetaspace() { 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) { // Add to this manager's list of chunks in use and current_chunk(). get_space_manager(mdtype)->add_chunk(chunk, true); --- 5072,5098 ---- return get_space_list(NonClassType)->contains(ptr); } // ClassLoaderMetaspace ! 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) { // Add to this manager's list of chunks in use and current_chunk(). get_space_manager(mdtype)->add_chunk(chunk, true);
*** 4820,4829 **** --- 5114,5125 ---- } 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); if (Metaspace::using_class_space()) { // Allocate SpaceManager for classes.
*** 4841,4850 **** --- 5137,5149 ---- } } 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); } else { return vsm()->allocate(word_size);
*** 4876,4923 **** } 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::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); } --- 5175,5184 ----
*** 4929,4938 **** --- 5190,5201 ---- void ClassLoaderMetaspace::deallocate(MetaWord* ptr, size_t word_size, bool is_class) { Metaspace::assert_not_frozen(); assert(!SafepointSynchronize::is_at_safepoint() || Thread::current()->is_VM_thread(), "should be the VM thread"); + DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_deallocs)); + MutexLockerEx ml(vsm()->lock(), Mutex::_no_safepoint_check_flag); if (is_class && Metaspace::using_class_space()) { class_vsm()->deallocate(ptr, word_size); } else {
*** 4960,4979 **** if (Metaspace::using_class_space()) { class_vsm()->verify(); } } ! void ClassLoaderMetaspace::dump(outputStream* const out) const { ! out->print_cr("\nVirtual space manager: " INTPTR_FORMAT, p2i(vsm())); ! vsm()->dump(out); if (Metaspace::using_class_space()) { ! out->print_cr("\nClass space manager: " INTPTR_FORMAT, p2i(class_vsm())); ! class_vsm()->dump(out); } } ! #ifdef ASSERT static void do_verify_chunk(Metachunk* chunk) { guarantee(chunk != NULL, "Sanity"); // Verify chunk itself; then verify that it is consistent with the --- 5223,5244 ---- if (Metaspace::using_class_space()) { class_vsm()->verify(); } } ! 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()) { ! 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) { guarantee(chunk != NULL, "Sanity"); // Verify chunk itself; then verify that it is consistent with the
*** 5315,5330 **** int num_humongous_chunks; }; 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; } struct chunk_geometry_t { size_t specialized_chunk_word_size; size_t small_chunk_word_size; --- 5580,5595 ---- int num_humongous_chunks; }; extern void test_metaspace_retrieve_chunkmanager_statistics(Metaspace::MetadataType mdType, chunkmanager_statistics_t* out) { ChunkManager* const chunk_manager = Metaspace::get_chunk_manager(mdType); ! ChunkManagerStatistics stat; chunk_manager->get_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 { size_t specialized_chunk_word_size; size_t small_chunk_word_size;
< prev index next >