diff -r 595fcbebaa77 -r 0e7d9a23261e src/hotspot/share/memory/metaspace/metachunk.cpp --- a/src/hotspot/share/memory/metaspace/metachunk.cpp Wed Sep 18 07:46:02 2019 +0200 +++ b/src/hotspot/share/memory/metaspace/metachunk.cpp Thu Sep 19 15:21:27 2019 +0200 @@ -30,8 +30,10 @@ #include "logging/log.hpp" #include "memory/metaspace/chunkLevel.hpp" #include "memory/metaspace/metachunk.hpp" +#include "memory/metaspace/metaDebug.hpp" #include "memory/metaspace/metaspaceCommon.hpp" #include "memory/metaspace/virtualSpaceNode.hpp" +#include "runtime/mutexLocker.hpp" #include "utilities/align.hpp" #include "utilities/copy.hpp" @@ -52,6 +54,12 @@ return '?'; } +#ifdef ASSERT +void Metachunk::assert_have_expand_lock() { + assert_lock_strong(MetaspaceExpand_lock); +} +#endif + // Commit uncommitted section of the chunk. // Fails if we hit a commit limit. bool Metachunk::commit_up_to(size_t new_committed_words) { @@ -222,6 +230,8 @@ _used_words += request_word_size; + SOMETIMES(verify(false);) + return p; } @@ -275,74 +285,70 @@ } } -volatile MetaWord dummy = 0; - -void Metachunk::verify(bool slow) const { - - assert(!is_dead(), "dead chunk."); - // Note: only call this on a life Metachunk. - chklvl::check_valid_level(level()); +// Verifies linking with neighbors in virtual space. +// Can only be done under expand lock protection. +void Metachunk::verify_neighborhood() const { - assert(base() != NULL, "No base ptr"); - assert(committed_words() >= used_words(), - "mismatch: committed: " SIZE_FORMAT ", used: " SIZE_FORMAT ".", - committed_words(), used_words()); - assert(word_size() >= committed_words(), - "mismatch: word_size: " SIZE_FORMAT ", committed: " SIZE_FORMAT ".", - word_size(), committed_words()); + assert_lock_strong(MetaspaceExpand_lock); + assert(!is_dead(), "Do not call on dead chunks."); - // Test base pointer - assert(vsnode() != NULL, "No space"); - vsnode()->check_pointer(base()); - assert(base() != NULL, "Base pointer NULL"); - - // Neighbors shall be adjacent to us... - if (prev_in_vs() != NULL) { - assert(prev_in_vs()->end() == base() && - prev_in_vs()->next_in_vs() == this, - "Chunk " METACHUNK_FORMAT ": broken link to left neighbor: " METACHUNK_FORMAT ".", - METACHUNK_FORMAT_ARGS(this), METACHUNK_FORMAT_ARGS(prev_in_vs())); - } + if (is_root_chunk()) { - if (next_in_vs() != NULL) { - assert(end() == next_in_vs()->base() && - next_in_vs()->prev_in_vs() == this, - "Chunk " METACHUNK_FORMAT ": broken link to right neighbor: " METACHUNK_FORMAT ".", - METACHUNK_FORMAT_ARGS(this), METACHUNK_FORMAT_ARGS(next_in_vs())); - } + // Root chunks are all alone in the world. + assert(next_in_vs() == NULL || prev_in_vs() == NULL, "Root chunks should have no neighbors"); - // Starting address shall be aligned to chunk size. - const size_t required_alignment = word_size() * sizeof(MetaWord); - assert_is_aligned(base(), required_alignment); + } else { - if (!is_root_chunk()) { + // Non-root chunks have neighbors, at least one, possibly two. assert(next_in_vs() != NULL || prev_in_vs() != NULL, "A non-root chunk should have neighbors (chunk @" PTR_FORMAT ", base " PTR_FORMAT ", level " CHKLVL_FORMAT ".", p2i(this), p2i(base()), level()); - // check buddy. Note: the chunk following us or preceeding us may - // be our buddy or a splintered part of it. + if (prev_in_vs() != NULL) { + assert(prev_in_vs()->end() == base(), + "Chunk " METACHUNK_FULL_FORMAT ": should be adjacent to predecessor: " METACHUNK_FULL_FORMAT ".", + METACHUNK_FULL_FORMAT_ARGS(this), METACHUNK_FULL_FORMAT_ARGS(prev_in_vs())); + assert(prev_in_vs()->next_in_vs() == this, + "Chunk " METACHUNK_FULL_FORMAT ": broken link to left neighbor: " METACHUNK_FULL_FORMAT " (" PTR_FORMAT ").", + METACHUNK_FULL_FORMAT_ARGS(this), METACHUNK_FULL_FORMAT_ARGS(prev_in_vs()), p2i(prev_in_vs()->next_in_vs())); + } + + if (next_in_vs() != NULL) { + assert(end() == next_in_vs()->base(), + "Chunk " METACHUNK_FULL_FORMAT ": should be adjacent to successor: " METACHUNK_FULL_FORMAT ".", + METACHUNK_FULL_FORMAT_ARGS(this), METACHUNK_FULL_FORMAT_ARGS(next_in_vs())); + assert(next_in_vs()->prev_in_vs() == this, + "Chunk " METACHUNK_FULL_FORMAT ": broken link to right neighbor: " METACHUNK_FULL_FORMAT " (" PTR_FORMAT ").", + METACHUNK_FULL_FORMAT_ARGS(this), METACHUNK_FULL_FORMAT_ARGS(next_in_vs()), p2i(next_in_vs()->prev_in_vs())); + } + + // One of the neighbors must be the buddy. It can be whole or splintered. + + // The chunk following us or preceeding us may be our buddy or a splintered part of it. Metachunk* buddy = is_leader() ? next_in_vs() : prev_in_vs(); assert(buddy != NULL, "Missing neighbor."); - assert(!buddy->is_dead(), "buddy dead."); + assert(!buddy->is_dead(), "Invalid buddy state."); // This neighbor is either or buddy (same level) or a splinter of our buddy - hence - // the level can never be smaller (larger chunk size). + // the level can never be smaller (aka the chunk size cannot be larger). assert(buddy->level() >= level(), "Wrong level."); + if (buddy->level() == level()) { - // we have a direct, unsplintered buddy. - assert(buddy->is_leader() == !is_leader(), "Only one chunk can be leader in a pair"); + // If the buddy is of the same size as us, it is unsplintered. + assert(buddy->is_leader() == !is_leader(), + "Only one chunk can be leader in a pair"); // When direct buddies are neighbors, one or both should be in use, otherwise they should // have been merged. - // Since we call verify() from internal functions where we are about to merge or just did split, - // do not test this. + // But since we call this verification function from internal functions where we are about to merge or just did split, + // do not test this. We have RootChunkArea::verify_area_is_ideally_merged() for testing that. + // assert(buddy->is_in_use() || is_in_use(), "incomplete merging?"); if (is_leader()) { @@ -352,15 +358,53 @@ assert(buddy->end() == base(), "Sanity"); assert(is_aligned(buddy->base(), word_size() * 2 * BytesPerWord), "Sanity"); } + } else { + // Buddy, but splintered, and this is a part of it. if (is_leader()) { assert(buddy->base() == end(), "Sanity"); } else { assert(buddy->end() > (base() - word_size()), "Sanity"); } + } } +} + +volatile MetaWord dummy = 0; + +void Metachunk::verify(bool slow) const { + + // Note. This should be called under CLD lock protection. + + // We can verify everything except the _prev_in_vs/_next_in_vs pair. + // This is because neighbor chunks may be added concurrently, so we cannot rely + // on the content of _next_in_vs/_prev_in_vs unless we have the expand lock. + + assert(!is_dead(), "Do not call on dead chunks."); + + // Note: only call this on a life Metachunk. + chklvl::check_valid_level(level()); + + assert(base() != NULL, "No base ptr"); + + assert(committed_words() >= used_words(), + "mismatch: committed: " SIZE_FORMAT ", used: " SIZE_FORMAT ".", + committed_words(), used_words()); + + assert(word_size() >= committed_words(), + "mismatch: word_size: " SIZE_FORMAT ", committed: " SIZE_FORMAT ".", + word_size(), committed_words()); + + // Test base pointer + assert(base() != NULL, "Base pointer NULL"); + assert(vsnode() != NULL, "No space"); + vsnode()->check_pointer(base()); + + // Starting address shall be aligned to chunk size. + const size_t required_alignment = word_size() * sizeof(MetaWord); + assert_is_aligned(base(), required_alignment); // If slow, test the committed area if (slow && _committed_words > 0) { @@ -399,7 +443,7 @@ return false; } -void MetachunkList::verify(bool slow) const { +void MetachunkList::verify() const { int num = 0; const Metachunk* last_c = NULL; for (const Metachunk* c = first(); c != NULL; c = c->next()) { @@ -407,9 +451,7 @@ assert(c->prev() == last_c, "Broken link to predecessor. Chunk " METACHUNK_FULL_FORMAT ".", METACHUNK_FULL_FORMAT_ARGS(c)); - if (slow) { - c->verify(false); - } + c->verify(false); last_c = c; } _num.check(num); @@ -458,7 +500,7 @@ } // Check each list. - list_for_level(l)->verify(slow); + list_for_level(l)->verify(); num += list_for_level(l)->size(); word_size += list_for_level(l)->size() * chklvl::word_size_for_level(l);