--- old/src/hotspot/share/memory/metaspace/rootChunkArea.cpp 2020-09-04 13:58:36.869671903 +0200 +++ /dev/null 2020-09-04 12:37:41.765504620 +0200 @@ -1,565 +0,0 @@ -/* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2020 SAP SE. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#include "precompiled.hpp" - -#include "logging/log.hpp" -#include "memory/allocation.hpp" -#include "memory/metaspace/chunkHeaderPool.hpp" -#include "memory/metaspace/chunkManager.hpp" -#include "memory/metaspace/freeChunkList.hpp" -#include "memory/metaspace/metachunk.hpp" -#include "memory/metaspace/metaspaceCommon.hpp" -#include "memory/metaspace/rootChunkArea.hpp" -#include "runtime/mutexLocker.hpp" -#include "utilities/debug.hpp" -#include "utilities/globalDefinitions.hpp" - -namespace metaspace { - -RootChunkArea::RootChunkArea(const MetaWord* base) - : _base(base), _first_chunk(NULL) -{} - -RootChunkArea::~RootChunkArea() { - // This is called when a VirtualSpaceNode is destructed (purged). - // All chunks should be free of course. In fact, there should only - // be one chunk, since all free chunks should have been merged. - if (_first_chunk != NULL) { - assert(_first_chunk->is_root_chunk() && _first_chunk->is_free(), - "Cannot delete root chunk area if not all chunks are free."); - ChunkHeaderPool::pool()->return_chunk_header(_first_chunk); - } -} - -// Initialize: allocate a root node and a root chunk header; return the -// root chunk header. It will be partly initialized. -// Note: this just allocates a memory-less header; memory itself is allocated inside VirtualSpaceNode. -Metachunk* RootChunkArea::alloc_root_chunk_header(VirtualSpaceNode* node) { - - assert(_first_chunk == 0, "already have a root"); - - Metachunk* c = ChunkHeaderPool::pool()->allocate_chunk_header(); - c->initialize(node, const_cast(_base), chunklevel::ROOT_CHUNK_LEVEL); - - _first_chunk = c; - - return c; - -} - -// Given a chunk c, split it recursively until you get a chunk of the given target_level. -// -// The resulting target chunk resides at the same address as the original chunk. -// The resulting splinters are added to freelists. -// -// Returns pointer to the result chunk; the splitted-off chunks are added as -// free chunks to the freelists. -void RootChunkArea::split(chunklevel_t target_level, Metachunk* c, FreeChunkListVector* freelists) { - - // Splitting a chunk once works like this: - // - // For a given chunk we want to split: - // - increase the chunk level (which halves its size) - // - (but leave base address as it is since it will be the leader of the newly - // created chunk pair) - // - then create a new chunk header of the same level, set its memory range - // to cover the second halfof the old chunk. - // - wire them up (prev_in_vs/next_in_vs) - // - return the follower chunk as "splinter chunk" in the splinters array. - - // Doing this multiple times will create a new free splinter chunk for every - // level we split: - // - // A <- original chunk - // - // B B <- split into two halves - // - // C C B <- first half split again - // - // D D C B <- first half split again ... - // - - // As an optimization, since we usually do not split once but multiple times, - // to not do each split separately, since we would have to wire up prev_in_vs/next_in_vs - // on every level just to tear it open in the next level when we reintroduce a new - // half chunk splinter. - // Instead, just split split split and delay building up the double linked list of the - // new chunks at the end of all splits. - - DEBUG_ONLY(check_pointer(c->base());) - DEBUG_ONLY(c->verify(false);) - assert(c->is_free(), "Can only split free chunks."); - - DEBUG_ONLY(chunklevel::check_valid_level(target_level)); - assert(target_level > c->level(), "Wrong target level"); - - const chunklevel_t starting_level = c->level(); - - while (c->level() < target_level) { - - log_trace(metaspace)("Splitting chunk: " METACHUNK_FULL_FORMAT ".", METACHUNK_FULL_FORMAT_ARGS(c)); - - c->inc_level(); - Metachunk* splinter_chunk = ChunkHeaderPool::pool()->allocate_chunk_header(); - splinter_chunk->initialize(c->vsnode(), c->end(), c->level()); - - // Fix committed words info: If over the half of the original chunk was - // committed, committed area spills over into the follower chunk. - const size_t old_committed_words = c->committed_words(); - if (old_committed_words > c->word_size()) { - c->set_committed_words(c->word_size()); - splinter_chunk->set_committed_words(old_committed_words - c->word_size()); - } else { - splinter_chunk->set_committed_words(0); - } - - // Insert splinter chunk into vs list - if (c->next_in_vs() != NULL) { - c->next_in_vs()->set_prev_in_vs(splinter_chunk); - } - splinter_chunk->set_next_in_vs(c->next_in_vs()); - splinter_chunk->set_prev_in_vs(c); - c->set_next_in_vs(splinter_chunk); - - log_trace(metaspace)(".. Result chunk: " METACHUNK_FULL_FORMAT ".", METACHUNK_FULL_FORMAT_ARGS(c)); - log_trace(metaspace)(".. Splinter chunk: " METACHUNK_FULL_FORMAT ".", METACHUNK_FULL_FORMAT_ARGS(splinter_chunk)); - - // Add splinter to free lists - freelists->add(splinter_chunk); - - } - - assert(c->level() == target_level, "Sanity"); - - DEBUG_ONLY(verify(true);) - DEBUG_ONLY(c->verify(true);) - -} - - -// Given a chunk, attempt to merge it recursively with its neighboring chunks. -// -// If successful (merged at least once), returns address of -// the merged chunk; NULL otherwise. -// -// The merged chunks are removed from the freelists. -// -// !!! Please note that if this method returns a non-NULL value, the -// original chunk will be invalid and should not be accessed anymore! !!! -Metachunk* RootChunkArea::merge(Metachunk* c, FreeChunkListVector* freelists) { - - // Note rules: - // - // - a chunk always has a buddy, unless it is a root chunk. - // - In that buddy pair, a chunk is either leader or follower. - // - a chunk's base address is always aligned at its size. - // - if chunk is leader, its base address is also aligned to the size of the next - // lower level, at least. A follower chunk is not. - - // How we merge once: - // - // For a given chunk c, which has to be free and non-root, we do: - // - find out if we are the leader or the follower chunk - // - if we are leader, next_in_vs must be the follower; if we are follower, - // prev_in_vs must be the leader. Now we have the buddy chunk. - // - However, if the buddy chunk itself is split (of a level higher than us) - // we cannot merge. - // - we can only merge if the buddy is of the same level as we are and it is - // free. - // - Then we merge by simply removing the follower chunk from the address range - // linked list (returning the now useless header to the pool) and decreasing - // the leader chunk level by one. That makes it double the size. - - // Example: - // (lower case chunks are free, the * indicates the chunk we want to merge): - // - // ........................ - // d d*c b A <- we return the second (d*) chunk... - // - // c* c b A <- we merge it with its predecessor and decrease its level... - // - // b* b A <- we merge it again, since its new neighbor was free too... - // - // a* A <- we merge it again, since its new neighbor was free too... - // - // And we are done, since its new neighbor, (A), is not free. We would also be done - // if the new neighbor itself is splintered. - - DEBUG_ONLY(check_pointer(c->base());) - assert(!c->is_root_chunk(), "Cannot be merged further."); - assert(c->is_free(), "Can only merge free chunks."); - - DEBUG_ONLY(c->verify(false);) - - log_trace(metaspace)("Attempting to merge chunk " METACHUNK_FORMAT ".", METACHUNK_FORMAT_ARGS(c)); - - const chunklevel_t starting_level = c->level(); - - bool stop = false; - Metachunk* result = NULL; - - do { - - // First find out if this chunk is the leader of its pair - const bool is_leader = c->is_leader(); - - // Note: this is either our buddy or a splinter of the buddy. - Metachunk* const buddy = c->is_leader() ? c->next_in_vs() : c->prev_in_vs(); - DEBUG_ONLY(buddy->verify(true);) - - // A buddy chunk must be of the same or higher level (so, same size or smaller) - // never be larger. - assert(buddy->level() >= c->level(), "Sanity"); - - // Is this really my buddy (same level) or a splinter of it (higher level)? - // Also, is it free? - if (buddy->level() != c->level() || buddy->is_free() == false) { - - log_trace(metaspace)("cannot merge with chunk " METACHUNK_FORMAT ".", METACHUNK_FORMAT_ARGS(buddy)); - - stop = true; - - } else { - - log_trace(metaspace)("will merge with chunk " METACHUNK_FORMAT ".", METACHUNK_FORMAT_ARGS(buddy)); - - // We can merge with the buddy. - - // First, remove buddy from the chunk manager. - assert(buddy->is_free(), "Sanity"); - freelists->remove(buddy); - - // Determine current leader and follower - Metachunk* leader; - Metachunk* follower; - if (is_leader) { - leader = c; follower = buddy; - } else { - leader = buddy; follower = c; - } - - // Last checkpoint - assert(leader->end() == follower->base() && - leader->level() == follower->level() && - leader->is_free() && follower->is_free(), "Sanity"); - - // The new merged chunk is as far committed as possible (if leader - // chunk is fully committed, as far as the follower chunk). - size_t merged_committed_words = leader->committed_words(); - if (merged_committed_words == leader->word_size()) { - merged_committed_words += follower->committed_words(); - } - - // Leader survives, follower chunk is freed. Remove follower from vslist .. - leader->set_next_in_vs(follower->next_in_vs()); - if (follower->next_in_vs() != NULL) { - follower->next_in_vs()->set_prev_in_vs(leader); - } - - // .. and return follower chunk header to pool for reuse. - ChunkHeaderPool::pool()->return_chunk_header(follower); - - // Leader level gets decreased (leader chunk doubles in size) but - // base address stays the same. - leader->dec_level(); - - // set commit boundary - leader->set_committed_words(merged_committed_words); - - // If the leader is now of root chunk size, stop merging - if (leader->is_root_chunk()) { - stop = true; - } - - result = c = leader; - - DEBUG_ONLY(leader->verify(true);) - - } - - } while (!stop); - -#ifdef ASSERT - verify(true); - if (result != NULL) { - result->verify(true); - } -#endif // ASSERT - - return result; - -} - -// Given a chunk c, which must be "in use" and must not be a root chunk, attempt to -// enlarge it in place by claiming its trailing buddy. -// -// This will only work if c is the leader of the buddy pair and the trailing buddy is free. -// -// If successful, the follower chunk will be removed from the freelists, the leader chunk c will -// double in size (level decreased by one). -// -// On success, true is returned, false otherwise. -bool RootChunkArea::attempt_enlarge_chunk(Metachunk* c, FreeChunkListVector* freelists) { - - DEBUG_ONLY(check_pointer(c->base());) - assert(!c->is_root_chunk(), "Cannot be merged further."); - - // There is no real reason for this limitation other than it is not - // needed on free chunks since they should be merged already: - assert(c->is_in_use(), "Can only enlarge in use chunks."); - - DEBUG_ONLY(c->verify(false);) - - if (!c->is_leader()) { - return false; - } - - // We are the leader, so the buddy must follow us. - Metachunk* const buddy = c->next_in_vs(); - DEBUG_ONLY(buddy->verify(true);) - - // Of course buddy cannot be larger than us. - assert(buddy->level() >= c->level(), "Sanity"); - - // We cannot merge buddy in if it is not free... - if (!buddy->is_free()) { - return false; - } - - // ... nor if it is splintered. - if (buddy->level() != c->level()) { - return false; - } - - // Okay, lets enlarge c. - - log_trace(metaspace)("Enlarging chunk " METACHUNK_FULL_FORMAT " by merging in follower " METACHUNK_FULL_FORMAT ".", - METACHUNK_FULL_FORMAT_ARGS(c), METACHUNK_FULL_FORMAT_ARGS(buddy)); - - // the enlarged c is as far committed as possible: - size_t merged_committed_words = c->committed_words(); - if (merged_committed_words == c->word_size()) { - merged_committed_words += buddy->committed_words(); - } - - // Remove buddy from vs list... - Metachunk* successor = buddy->next_in_vs(); - if (successor != NULL) { - successor->set_prev_in_vs(c); - } - c->set_next_in_vs(successor); - - // .. and from freelist ... - freelists->remove(buddy); - - // .. and return its empty husk to the pool... - ChunkHeaderPool::pool()->return_chunk_header(buddy); - - // Then decrease level of c. - c->dec_level(); - - // and correct committed words if needed. - c->set_committed_words(merged_committed_words); - - log_debug(metaspace)("Enlarged chunk " METACHUNK_FULL_FORMAT ".", METACHUNK_FULL_FORMAT_ARGS(c)); -// log_debug(metaspace)("Enlarged chunk " METACHUNK_FORMAT ".", METACHUNK_FORMAT_ARGS(c)); - - DEBUG_ONLY(verify(true)); - - return true; - -} - -// Returns true if this root chunk area is completely free: -// In that case, it should only contain one chunk (maximally merged, so a root chunk) -// and it should be free. -bool RootChunkArea::is_free() const { - return _first_chunk == NULL || - (_first_chunk->is_root_chunk() && _first_chunk->is_free()); -} - - -#ifdef ASSERT - -#define assrt_(cond, ...) \ - if (!(cond)) { \ - fdStream errst(2); \ - this->print_on(&errst); \ - vmassert(cond, __VA_ARGS__); \ - } - -void RootChunkArea::verify(bool slow) const { - - - assert_lock_strong(MetaspaceExpand_lock); - assert_is_aligned(_base, chunklevel::MAX_CHUNK_BYTE_SIZE); - - // Iterate thru all chunks in this area. They must be ordered correctly, - // being adjacent to each other, and cover the complete area - int num_chunk = 0; - - if (_first_chunk != NULL) { - - assrt_(_first_chunk->prev_in_vs() == NULL, "Sanity"); - - const Metachunk* c = _first_chunk; - const MetaWord* expected_next_base = _base; - const MetaWord* const area_end = _base + word_size(); - - while (c != NULL) { - - assrt_(c->is_free() || c->is_in_use(), - "Chunk No. %d " METACHUNK_FORMAT " - invalid state.", - num_chunk, METACHUNK_FORMAT_ARGS(c)); - - assrt_(c->base() == expected_next_base, - "Chunk No. %d " METACHUNK_FORMAT " - unexpected base.", - num_chunk, METACHUNK_FORMAT_ARGS(c)); - - assrt_(c->base() >= base() && c->end() <= end(), - "chunk %d " METACHUNK_FORMAT " oob for this root area [" PTR_FORMAT ".." PTR_FORMAT ").", - num_chunk, METACHUNK_FORMAT_ARGS(c), p2i(base()), p2i(end())); - - assrt_(is_aligned(c->base(), c->word_size()), - "misaligned chunk %d " METACHUNK_FORMAT ".", num_chunk, METACHUNK_FORMAT_ARGS(c)); - - c->verify_neighborhood(); - c->verify(slow); - - expected_next_base = c->end(); - num_chunk ++; - - c = c->next_in_vs(); - - } - assrt_(expected_next_base == _base + word_size(), "Sanity"); - } - -} - -void RootChunkArea::verify_area_is_ideally_merged() const { - - SOMETIMES(assert_lock_strong(MetaspaceExpand_lock);) - - int num_chunk = 0; - for (const Metachunk* c = _first_chunk; c != NULL; c = c->next_in_vs()) { - if (!c->is_root_chunk() && c->is_free()) { - // If a chunk is free, it must not have a buddy which is also free, because - // those chunks should have been merged. - // In other words, a buddy shall be either in-use or splintered - // (which in turn would mean part of it are in use). - Metachunk* const buddy = c->is_leader() ? c->next_in_vs() : c->prev_in_vs(); - assrt_(buddy->is_in_use() || buddy->level() > c->level(), - "Chunk No. %d " METACHUNK_FORMAT " : missed merge opportunity with neighbor " METACHUNK_FORMAT ".", - num_chunk, METACHUNK_FORMAT_ARGS(c), METACHUNK_FORMAT_ARGS(buddy)); - } - num_chunk ++; - } -} - -#endif - -void RootChunkArea::print_on(outputStream* st) const { - - st->print(PTR_FORMAT ": ", p2i(base())); - if (_first_chunk != NULL) { - const Metachunk* c = _first_chunk; - // 01234567890123 - const char* letters_for_levels_cap = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - const char* letters_for_levels = "abcdefghijklmnopqrstuvwxyz"; - while (c != NULL) { - const chunklevel_t l = c->level(); - if (l >= 0 && (size_t)l < strlen(letters_for_levels)) { -// c->print_on(st); st->cr(); - st->print("%c", c->is_free() ? letters_for_levels[c->level()] : letters_for_levels_cap[c->level()]); - } else { - // Obviously garbage, but lets not crash. - st->print("?"); - } - c = c->next_in_vs(); - } - } else { - st->print(" (no chunks)"); - } - st->cr(); - -} - - -// Create an array of ChunkTree objects, all initialized to NULL, covering -// a given memory range. Memory range must be a multiple of root chunk size. -RootChunkAreaLUT::RootChunkAreaLUT(const MetaWord* base, size_t word_size) - : _base(base), - _num((int)(word_size / chunklevel::MAX_CHUNK_WORD_SIZE)), - _arr(NULL) -{ - assert_is_aligned(word_size, chunklevel::MAX_CHUNK_WORD_SIZE); - _arr = NEW_C_HEAP_ARRAY(RootChunkArea, _num, mtClass); - const MetaWord* this_base = _base; - for (int i = 0; i < _num; i ++) { - RootChunkArea* rca = new(_arr + i) RootChunkArea(this_base); - assert(rca == _arr + i, "Sanity"); - this_base += chunklevel::MAX_CHUNK_WORD_SIZE; - } -} - -RootChunkAreaLUT::~RootChunkAreaLUT() { - for (int i = 0; i < _num; i ++) { - _arr[i].~RootChunkArea(); - } - FREE_C_HEAP_ARRAY(RootChunkArea, _arr); -} - -// Returns true if all areas in this area table are free (only contain free chunks). -bool RootChunkAreaLUT::is_free() const { - for (int i = 0; i < _num; i ++) { - if (!_arr[i].is_free()) { - return false; - } - } - return true; -} - -#ifdef ASSERT - -void RootChunkAreaLUT::verify(bool slow) const { - for (int i = 0; i < _num; i ++) { - check_pointer(_arr[i].base()); - _arr[i].verify(slow); - } -} - -#endif - -void RootChunkAreaLUT::print_on(outputStream* st) const { - for (int i = 0; i < _num; i ++) { - st->print("%2d:", i); - _arr[i].print_on(st); - } -} - - -} // end: namespace metaspace