Path: blob/master/src/hotspot/share/memory/metaspace/rootChunkArea.cpp
40957 views
/*1* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.2* Copyright (c) 2020, 2021 SAP SE. All rights reserved.3* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.4*5* This code is free software; you can redistribute it and/or modify it6* under the terms of the GNU General Public License version 2 only, as7* published by the Free Software Foundation.8*9* This code is distributed in the hope that it will be useful, but WITHOUT10* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or11* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License12* version 2 for more details (a copy is included in the LICENSE file that13* accompanied this code).14*15* You should have received a copy of the GNU General Public License version16* 2 along with this work; if not, write to the Free Software Foundation,17* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.18*19* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA20* or visit www.oracle.com if you need additional information or have any21* questions.22*23*/2425#include "precompiled.hpp"26#include "logging/log.hpp"27#include "memory/allocation.hpp"28#include "memory/metaspace/chunkHeaderPool.hpp"29#include "memory/metaspace/chunkManager.hpp"30#include "memory/metaspace/freeChunkList.hpp"31#include "memory/metaspace/metachunk.hpp"32#include "memory/metaspace/metaspaceCommon.hpp"33#include "memory/metaspace/rootChunkArea.hpp"34#include "runtime/mutexLocker.hpp"35#include "utilities/debug.hpp"36#include "utilities/globalDefinitions.hpp"3738namespace metaspace {3940RootChunkArea::RootChunkArea(const MetaWord* base) :41_base(base),42_first_chunk(NULL)43{}4445RootChunkArea::~RootChunkArea() {46// This is called when a VirtualSpaceNode is destructed (purged).47// All chunks should be free of course. In fact, there should only48// be one chunk, since all free chunks should have been merged.49if (_first_chunk != NULL) {50assert(_first_chunk->is_root_chunk() && _first_chunk->is_free(),51"Cannot delete root chunk area if not all chunks are free.");52ChunkHeaderPool::pool()->return_chunk_header(_first_chunk);53}54}5556// Initialize: allocate a root node and a root chunk header; return the57// root chunk header. It will be partly initialized.58// Note: this just allocates a memory-less header; memory itself is allocated inside VirtualSpaceNode.59Metachunk* RootChunkArea::alloc_root_chunk_header(VirtualSpaceNode* node) {60assert(_first_chunk == 0, "already have a root");61Metachunk* c = ChunkHeaderPool::pool()->allocate_chunk_header();62c->initialize(node, const_cast<MetaWord*>(_base), chunklevel::ROOT_CHUNK_LEVEL);63_first_chunk = c;64return c;65}6667// Given a chunk c, split it recursively until you get a chunk of the given target_level.68//69// The resulting target chunk resides at the same address as the original chunk.70// The resulting splinters are added to freelists.71//72// Returns pointer to the result chunk; the splitted-off chunks are added as73// free chunks to the freelists.74void RootChunkArea::split(chunklevel_t target_level, Metachunk* c, FreeChunkListVector* freelists) {75// Splitting a chunk once works like this:76//77// For a given chunk we want to split:78// - increase the chunk level (which halves its size)79// - (but leave base address as it is since it will be the leader of the newly80// created chunk pair)81// - then create a new chunk header of the same level, set its memory range82// to cover the second half of the old chunk.83// - wire them up (prev_in_vs/next_in_vs)84// - return the follower chunk as "splinter chunk" in the splinters array.8586// Doing this multiple times will create a new free splinter chunk for every87// level we split:88//89// A <- original chunk90//91// B B <- split into two halves92//93// C C B <- first half split again94//95// D D C B <- first half split again ...96//9798DEBUG_ONLY(check_pointer(c->base());)99DEBUG_ONLY(c->verify();)100assert(c->is_free(), "Can only split free chunks.");101102DEBUG_ONLY(chunklevel::check_valid_level(target_level));103assert(target_level > c->level(), "Wrong target level");104105const chunklevel_t starting_level = c->level();106107while (c->level() < target_level) {108109log_trace(metaspace)("Splitting chunk: " METACHUNK_FULL_FORMAT ".", METACHUNK_FULL_FORMAT_ARGS(c));110111c->inc_level();112Metachunk* splinter_chunk = ChunkHeaderPool::pool()->allocate_chunk_header();113splinter_chunk->initialize(c->vsnode(), c->end(), c->level());114115// Fix committed words info: If over the half of the original chunk was116// committed, committed area spills over into the follower chunk.117const size_t old_committed_words = c->committed_words();118if (old_committed_words > c->word_size()) {119c->set_committed_words(c->word_size());120splinter_chunk->set_committed_words(old_committed_words - c->word_size());121} else {122splinter_chunk->set_committed_words(0);123}124125// Insert splinter chunk into vs list126if (c->next_in_vs() != NULL) {127c->next_in_vs()->set_prev_in_vs(splinter_chunk);128}129splinter_chunk->set_next_in_vs(c->next_in_vs());130splinter_chunk->set_prev_in_vs(c);131c->set_next_in_vs(splinter_chunk);132133log_trace(metaspace)(".. Result chunk: " METACHUNK_FULL_FORMAT ".", METACHUNK_FULL_FORMAT_ARGS(c));134log_trace(metaspace)(".. Splinter chunk: " METACHUNK_FULL_FORMAT ".", METACHUNK_FULL_FORMAT_ARGS(splinter_chunk));135136// Add splinter to free lists137freelists->add(splinter_chunk);138}139140assert(c->level() == target_level, "Sanity");141142DEBUG_ONLY(verify();)143DEBUG_ONLY(c->verify();)144}145146// Given a chunk, attempt to merge it recursively with its neighboring chunks.147//148// If successful (merged at least once), returns address of149// the merged chunk; NULL otherwise.150//151// The merged chunks are removed from the freelists.152//153// !!! Please note that if this method returns a non-NULL value, the154// original chunk will be invalid and should not be accessed anymore! !!!155Metachunk* RootChunkArea::merge(Metachunk* c, FreeChunkListVector* freelists) {156// Note rules:157//158// - a chunk always has a buddy, unless it is a root chunk.159// - In that buddy pair, a chunk is either leader or follower.160// - a chunk's base address is always aligned at its size.161// - if chunk is leader, its base address is also aligned to the size of the next162// lower level, at least. A follower chunk is not.163164// How we merge once:165//166// For a given chunk c, which has to be free and non-root, we do:167// - find out if we are the leader or the follower chunk168// - if we are leader, next_in_vs must be the follower; if we are follower,169// prev_in_vs must be the leader. Now we have the buddy chunk.170// - However, if the buddy chunk itself is split (of a level higher than us)171// we cannot merge.172// - we can only merge if the buddy is of the same level as we are and it is173// free.174// - Then we merge by simply removing the follower chunk from the address range175// linked list (returning the now useless header to the pool) and decreasing176// the leader chunk level by one. That makes it double the size.177178// Example:179// (lower case chunks are free, the * indicates the chunk we want to merge):180//181// ........................182// d d*c b A <- we return the second (d*) chunk...183//184// c* c b A <- we merge it with its predecessor and decrease its level...185//186// b* b A <- we merge it again, since its new neighbor was free too...187//188// a* A <- we merge it again, since its new neighbor was free too...189//190// And we are done, since its new neighbor, (A), is not free. We would also be done191// if the new neighbor itself is splintered.192193DEBUG_ONLY(check_pointer(c->base());)194assert(!c->is_root_chunk(), "Cannot be merged further.");195assert(c->is_free(), "Can only merge free chunks.");196197DEBUG_ONLY(c->verify();)198199log_trace(metaspace)("Attempting to merge chunk " METACHUNK_FORMAT ".", METACHUNK_FORMAT_ARGS(c));200201const chunklevel_t starting_level = c->level();202203bool stop = false;204Metachunk* result = NULL;205206do {207208// First find out if this chunk is the leader of its pair209const bool is_leader = c->is_leader();210211// Note: this is either our buddy or a splinter of the buddy.212Metachunk* const buddy = c->is_leader() ? c->next_in_vs() : c->prev_in_vs();213DEBUG_ONLY(buddy->verify();)214215// A buddy chunk must be of the same or higher level (so, same size or smaller)216// never be larger.217assert(buddy->level() >= c->level(), "Sanity");218219// Is this really my buddy (same level) or a splinter of it (higher level)?220// Also, is it free?221if (buddy->level() != c->level() || buddy->is_free() == false) {222log_trace(metaspace)("cannot merge with chunk " METACHUNK_FORMAT ".", METACHUNK_FORMAT_ARGS(buddy));223stop = true;224} else {225log_trace(metaspace)("will merge with chunk " METACHUNK_FORMAT ".", METACHUNK_FORMAT_ARGS(buddy));226227// We can merge with the buddy.228// First, remove buddy from the chunk manager.229assert(buddy->is_free(), "Sanity");230freelists->remove(buddy);231232// Determine current leader and follower233Metachunk* leader;234Metachunk* follower;235if (is_leader) {236leader = c; follower = buddy;237} else {238leader = buddy; follower = c;239}240241// Last checkpoint242assert(leader->end() == follower->base() &&243leader->level() == follower->level() &&244leader->is_free() && follower->is_free(), "Sanity");245246// The new merged chunk is as far committed as possible (if leader247// chunk is fully committed, as far as the follower chunk).248size_t merged_committed_words = leader->committed_words();249if (merged_committed_words == leader->word_size()) {250merged_committed_words += follower->committed_words();251}252253// Leader survives, follower chunk is freed. Remove follower from vslist ..254leader->set_next_in_vs(follower->next_in_vs());255if (follower->next_in_vs() != NULL) {256follower->next_in_vs()->set_prev_in_vs(leader);257}258259// .. and return follower chunk header to pool for reuse.260ChunkHeaderPool::pool()->return_chunk_header(follower);261262// Leader level gets decreased (leader chunk doubles in size) but263// base address stays the same.264leader->dec_level();265266// set commit boundary267leader->set_committed_words(merged_committed_words);268269// If the leader is now of root chunk size, stop merging270if (leader->is_root_chunk()) {271stop = true;272}273274result = c = leader;275DEBUG_ONLY(leader->verify();)276}277} while (!stop);278279#ifdef ASSERT280verify();281if (result != NULL) {282result->verify();283}284#endif // ASSERT285return result;286}287288// Given a chunk c, which must be "in use" and must not be a root chunk, attempt to289// enlarge it in place by claiming its trailing buddy.290//291// This will only work if c is the leader of the buddy pair and the trailing buddy is free.292//293// If successful, the follower chunk will be removed from the freelists, the leader chunk c will294// double in size (level decreased by one).295//296// On success, true is returned, false otherwise.297bool RootChunkArea::attempt_enlarge_chunk(Metachunk* c, FreeChunkListVector* freelists) {298DEBUG_ONLY(check_pointer(c->base());)299assert(!c->is_root_chunk(), "Cannot be merged further.");300301// There is no real reason for this limitation other than it is not302// needed on free chunks since they should be merged already:303assert(c->is_in_use(), "Can only enlarge in use chunks.");304DEBUG_ONLY(c->verify();)305306if (!c->is_leader()) {307return false;308}309310// We are the leader, so the buddy must follow us.311Metachunk* const buddy = c->next_in_vs();312DEBUG_ONLY(buddy->verify();)313314// Of course buddy cannot be larger than us.315assert(buddy->level() >= c->level(), "Sanity");316317// We cannot merge buddy in if it is not free...318if (!buddy->is_free()) {319return false;320}321// ... nor if it is splintered.322if (buddy->level() != c->level()) {323return false;324}325326// Okay, lets enlarge c.327log_trace(metaspace)("Enlarging chunk " METACHUNK_FULL_FORMAT " by merging in follower " METACHUNK_FULL_FORMAT ".",328METACHUNK_FULL_FORMAT_ARGS(c), METACHUNK_FULL_FORMAT_ARGS(buddy));329330// the enlarged c is as far committed as possible:331size_t merged_committed_words = c->committed_words();332if (merged_committed_words == c->word_size()) {333merged_committed_words += buddy->committed_words();334}335336// Remove buddy from vs list...337Metachunk* successor = buddy->next_in_vs();338if (successor != NULL) {339successor->set_prev_in_vs(c);340}341c->set_next_in_vs(successor);342343// .. and from freelist ...344freelists->remove(buddy);345346// .. and return its empty husk to the pool...347ChunkHeaderPool::pool()->return_chunk_header(buddy);348349// Then decrease level of c.350c->dec_level();351352// and correct committed words if needed.353c->set_committed_words(merged_committed_words);354355log_debug(metaspace)("Enlarged chunk " METACHUNK_FULL_FORMAT ".", METACHUNK_FULL_FORMAT_ARGS(c));356357DEBUG_ONLY(verify());358return true;359}360361// Returns true if this root chunk area is completely free:362// In that case, it should only contain one chunk (maximally merged, so a root chunk)363// and it should be free.364bool RootChunkArea::is_free() const {365return _first_chunk == NULL ||366(_first_chunk->is_root_chunk() && _first_chunk->is_free());367}368369#ifdef ASSERT370371#define assrt_(cond, ...) \372if (!(cond)) { \373fdStream errst(2); \374this->print_on(&errst); \375vmassert(cond, __VA_ARGS__); \376}377378void RootChunkArea::verify() const {379assert_lock_strong(Metaspace_lock);380assert_is_aligned(_base, chunklevel::MAX_CHUNK_BYTE_SIZE);381382// Iterate thru all chunks in this area. They must be ordered correctly,383// being adjacent to each other, and cover the complete area384int num_chunk = 0;385386if (_first_chunk != NULL) {387assrt_(_first_chunk->prev_in_vs() == NULL, "Sanity");388389const Metachunk* c = _first_chunk;390const MetaWord* expected_next_base = _base;391const MetaWord* const area_end = _base + word_size();392393while (c != NULL) {394assrt_(c->is_free() || c->is_in_use(),395"Chunk No. %d " METACHUNK_FORMAT " - invalid state.",396num_chunk, METACHUNK_FORMAT_ARGS(c));397assrt_(c->base() == expected_next_base,398"Chunk No. %d " METACHUNK_FORMAT " - unexpected base.",399num_chunk, METACHUNK_FORMAT_ARGS(c));400assrt_(c->base() >= base() && c->end() <= end(),401"chunk %d " METACHUNK_FORMAT " oob for this root area [" PTR_FORMAT ".." PTR_FORMAT ").",402num_chunk, METACHUNK_FORMAT_ARGS(c), p2i(base()), p2i(end()));403assrt_(is_aligned(c->base(), c->word_size()),404"misaligned chunk %d " METACHUNK_FORMAT ".", num_chunk, METACHUNK_FORMAT_ARGS(c));405406c->verify_neighborhood();407c->verify();408expected_next_base = c->end();409num_chunk++;410c = c->next_in_vs();411}412assrt_(expected_next_base == _base + word_size(), "Sanity");413}414}415416void RootChunkArea::verify_area_is_ideally_merged() const {417SOMETIMES(assert_lock_strong(Metaspace_lock);)418int num_chunk = 0;419for (const Metachunk* c = _first_chunk; c != NULL; c = c->next_in_vs()) {420if (!c->is_root_chunk() && c->is_free()) {421// If a chunk is free, it must not have a buddy which is also free, because422// those chunks should have been merged.423// In other words, a buddy shall be either in-use or splintered424// (which in turn would mean part of it are in use).425Metachunk* const buddy = c->is_leader() ? c->next_in_vs() : c->prev_in_vs();426assrt_(buddy->is_in_use() || buddy->level() > c->level(),427"Chunk No. %d " METACHUNK_FORMAT " : missed merge opportunity with neighbor " METACHUNK_FORMAT ".",428num_chunk, METACHUNK_FORMAT_ARGS(c), METACHUNK_FORMAT_ARGS(buddy));429}430num_chunk++;431}432}433434#endif435436void RootChunkArea::print_on(outputStream* st) const {437st->print(PTR_FORMAT ": ", p2i(base()));438if (_first_chunk != NULL) {439const Metachunk* c = _first_chunk;440// 01234567890123441const char* letters_for_levels_cap = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";442const char* letters_for_levels = "abcdefghijklmnopqrstuvwxyz";443while (c != NULL) {444const chunklevel_t l = c->level();445if (l >= 0 && (size_t)l < strlen(letters_for_levels)) {446st->print("%c", c->is_free() ? letters_for_levels[c->level()] : letters_for_levels_cap[c->level()]);447} else {448// Obviously garbage, but lets not crash.449st->print("?");450}451c = c->next_in_vs();452}453} else {454st->print(" (no chunks)");455}456st->cr();457}458459// Create an array of ChunkTree objects, all initialized to NULL, covering460// a given memory range. Memory range must be a multiple of root chunk size.461RootChunkAreaLUT::RootChunkAreaLUT(const MetaWord* base, size_t word_size) :462_base(base),463_num((int)(word_size / chunklevel::MAX_CHUNK_WORD_SIZE)),464_arr(NULL)465{466assert_is_aligned(word_size, chunklevel::MAX_CHUNK_WORD_SIZE);467_arr = NEW_C_HEAP_ARRAY(RootChunkArea, _num, mtClass);468const MetaWord* this_base = _base;469for (int i = 0; i < _num; i++) {470RootChunkArea* rca = new(_arr + i) RootChunkArea(this_base);471assert(rca == _arr + i, "Sanity");472this_base += chunklevel::MAX_CHUNK_WORD_SIZE;473}474}475476RootChunkAreaLUT::~RootChunkAreaLUT() {477for (int i = 0; i < _num; i++) {478_arr[i].~RootChunkArea();479}480FREE_C_HEAP_ARRAY(RootChunkArea, _arr);481}482483// Returns true if all areas in this area table are free (only contain free chunks).484bool RootChunkAreaLUT::is_free() const {485for (int i = 0; i < _num; i++) {486if (!_arr[i].is_free()) {487return false;488}489}490return true;491}492493#ifdef ASSERT494495void RootChunkAreaLUT::verify() const {496for (int i = 0; i < _num; i++) {497check_pointer(_arr[i].base());498_arr[i].verify();499}500}501502#endif503504void RootChunkAreaLUT::print_on(outputStream* st) const {505for (int i = 0; i < _num; i++) {506st->print("%2d:", i);507_arr[i].print_on(st);508}509}510511} // end: namespace metaspace512513514