8189864: Provide an ascii map to visualize metaspace fragmentation
Reviewed-by: goetz, coleenp
--- a/src/hotspot/share/memory/metachunk.cpp Tue Nov 07 22:05:44 2017 -0800
+++ b/src/hotspot/share/memory/metachunk.cpp Tue Oct 24 14:34:14 2017 +0200
@@ -55,8 +55,8 @@
_container(container)
{
_top = initial_top();
+ set_is_tagged_free(false);
#ifdef ASSERT
- set_is_tagged_free(false);
mangle(uninitMetaWordVal);
#endif
}
--- a/src/hotspot/share/memory/metachunk.hpp Tue Nov 07 22:05:44 2017 -0800
+++ b/src/hotspot/share/memory/metachunk.hpp Tue Oct 24 14:34:14 2017 +0200
@@ -102,7 +102,7 @@
// Current allocation top.
MetaWord* _top;
- DEBUG_ONLY(bool _is_tagged_free;)
+ bool _is_tagged_free;
MetaWord* initial_top() const { return (MetaWord*)this + overhead(); }
MetaWord* top() const { return _top; }
@@ -138,10 +138,8 @@
size_t used_word_size() const;
size_t free_word_size() const;
-#ifdef ASSERT
bool is_tagged_free() { return _is_tagged_free; }
void set_is_tagged_free(bool v) { _is_tagged_free = v; }
-#endif
bool contains(const void* ptr) { return bottom() <= ptr && ptr < _top; }
--- a/src/hotspot/share/memory/metaspace.cpp Tue Nov 07 22:05:44 2017 -0800
+++ b/src/hotspot/share/memory/metaspace.cpp Tue Oct 24 14:34:14 2017 +0200
@@ -492,6 +492,7 @@
#endif
void print_on(outputStream* st) const;
+ void print_map(outputStream* st, bool is_class) const;
};
#define assert_is_aligned(value, alignment) \
@@ -543,6 +544,94 @@
}
}
+void VirtualSpaceNode::print_map(outputStream* st, bool is_class) const {
+
+ // Format:
+ // <ptr>
+ // <ptr> . .. . . ..
+ // SSxSSMMMMMMMMMMMMMMMMsssXX
+ // 112114444444444444444
+ // <ptr> . .. . . ..
+ // SSxSSMMMMMMMMMMMMMMMMsssXX
+ // 112114444444444444444
+
+ if (bottom() == top()) {
+ return;
+ }
+
+ // First line: dividers for every med-chunk-sized interval
+ // Second line: a dot for the start of a chunk
+ // Third line: a letter per chunk type (x,s,m,h), uppercase if in use.
+
+ const size_t spec_chunk_size = is_class ? ClassSpecializedChunk : SpecializedChunk;
+ const size_t small_chunk_size = is_class ? ClassSmallChunk : SmallChunk;
+ const size_t med_chunk_size = is_class ? ClassMediumChunk : MediumChunk;
+
+ int line_len = 100;
+ const size_t section_len = align_up(spec_chunk_size * line_len, med_chunk_size);
+ line_len = (int)(section_len / spec_chunk_size);
+
+ char* line1 = (char*)os::malloc(line_len, mtInternal);
+ char* line2 = (char*)os::malloc(line_len, mtInternal);
+ char* line3 = (char*)os::malloc(line_len, mtInternal);
+ int pos = 0;
+ const MetaWord* p = bottom();
+ const Metachunk* chunk = (const Metachunk*)p;
+ const MetaWord* chunk_end = p + chunk->word_size();
+ while (p < top()) {
+ if (pos == line_len) {
+ pos = 0;
+ st->fill_to(22);
+ st->print_raw(line1, line_len);
+ st->cr();
+ st->fill_to(22);
+ st->print_raw(line2, line_len);
+ st->cr();
+ }
+ if (pos == 0) {
+ st->print(PTR_FORMAT ":", p2i(p));
+ }
+ if (p == chunk_end) {
+ chunk = (Metachunk*)p;
+ chunk_end = p + chunk->word_size();
+ }
+ if (p == (const MetaWord*)chunk) {
+ // chunk starts.
+ line1[pos] = '.';
+ } else {
+ line1[pos] = ' ';
+ }
+ // Line 2: chunk type (x=spec, s=small, m=medium, h=humongous), uppercase if
+ // chunk is in use.
+ const bool chunk_is_free = ((Metachunk*)chunk)->is_tagged_free();
+ if (chunk->word_size() == spec_chunk_size) {
+ line2[pos] = chunk_is_free ? 'x' : 'X';
+ } else if (chunk->word_size() == small_chunk_size) {
+ line2[pos] = chunk_is_free ? 's' : 'S';
+ } else if (chunk->word_size() == med_chunk_size) {
+ line2[pos] = chunk_is_free ? 'm' : 'M';
+ } else if (chunk->word_size() > med_chunk_size) {
+ line2[pos] = chunk_is_free ? 'h' : 'H';
+ } else {
+ ShouldNotReachHere();
+ }
+ p += spec_chunk_size;
+ pos ++;
+ }
+ if (pos > 0) {
+ st->fill_to(22);
+ st->print_raw(line1, pos);
+ st->cr();
+ st->fill_to(22);
+ st->print_raw(line2, pos);
+ st->cr();
+ }
+ os::free(line1);
+ os::free(line2);
+ os::free(line3);
+}
+
+
#ifdef ASSERT
uintx VirtualSpaceNode::container_count_slow() {
uintx count = 0;
@@ -649,6 +738,7 @@
void purge(ChunkManager* chunk_manager);
void print_on(outputStream* st) const;
+ void print_map(outputStream* st) const;
class VirtualSpaceListIterator : public StackObj {
VirtualSpaceNode* _virtual_spaces;
@@ -1461,6 +1551,18 @@
}
}
+void VirtualSpaceList::print_map(outputStream* st) const {
+ VirtualSpaceNode* list = virtual_space_list();
+ VirtualSpaceListIterator iter(list);
+ unsigned i = 0;
+ while (iter.repeat()) {
+ st->print_cr("Node %u:", i);
+ VirtualSpaceNode* node = iter.get_next();
+ node->print_map(st, this->is_class());
+ i ++;
+ }
+}
+
// MetaspaceGC methods
// VM_CollectForMetadataAllocation is the vm operation used to GC.
@@ -1934,11 +2036,10 @@
// Remove it from the links to this freelist
chunk->set_next(NULL);
chunk->set_prev(NULL);
-#ifdef ASSERT
+
// Chunk is no longer on any freelist. Setting to false make container_count_slow()
// work.
chunk->set_is_tagged_free(false);
-#endif
chunk->container()->inc_container_count();
slow_locked_verify();
@@ -2006,7 +2107,7 @@
chunk_size_name(index), p2i(chunk), chunk->word_size());
}
chunk->container()->dec_container_count();
- DEBUG_ONLY(chunk->set_is_tagged_free(true);)
+ chunk->set_is_tagged_free(true);
// Chunk has been added; update counters.
account_for_added_chunk(chunk);
@@ -2945,9 +3046,9 @@
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
+ 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
+ " capacity in free chunks " SIZE_FORMAT "K) = " SIZE_FORMAT
"K capacity in allocated chunks " SIZE_FORMAT "K",
used_bytes / K,
free_bytes / K,
@@ -3211,6 +3312,31 @@
print_waste(out);
}
+// Prints an ASCII representation of the given space.
+void MetaspaceAux::print_metaspace_map(outputStream* out, Metaspace::MetadataType mdtype) {
+ MutexLockerEx cl(SpaceManager::expand_lock(), Mutex::_no_safepoint_check_flag);
+ const bool for_class = mdtype == Metaspace::ClassType ? true : false;
+ VirtualSpaceList* const vsl = for_class ? Metaspace::class_space_list() : Metaspace::space_list();
+ if (vsl != NULL) {
+ if (for_class) {
+ if (!Metaspace::using_class_space()) {
+ out->print_cr("No Class Space.");
+ return;
+ }
+ out->print_raw("---- Metaspace Map (Class Space) ----");
+ } else {
+ out->print_raw("---- Metaspace Map (Non-Class Space) ----");
+ }
+ // Print legend:
+ out->cr();
+ out->print_cr("Chunk Types (uppercase chunks are in use): x-specialized, s-small, m-medium, h-humongous.");
+ out->cr();
+ VirtualSpaceList* const vsl = for_class ? Metaspace::class_space_list() : Metaspace::space_list();
+ vsl->print_map(out);
+ out->cr();
+ }
+}
+
void MetaspaceAux::verify_free_chunks() {
Metaspace::chunk_manager_metadata()->verify();
if (Metaspace::using_class_space()) {
@@ -3849,6 +3975,7 @@
}
LogStream ls(log.info());
MetaspaceAux::dump(&ls);
+ MetaspaceAux::print_metaspace_map(&ls, mdtype);
ChunkManager::print_all_chunkmanagers(&ls);
}
--- a/src/hotspot/share/memory/metaspace.hpp Tue Nov 07 22:05:44 2017 -0800
+++ b/src/hotspot/share/memory/metaspace.hpp Tue Oct 24 14:34:14 2017 +0200
@@ -361,6 +361,10 @@
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