Path: blob/master/src/hotspot/share/memory/metaspace/metachunk.hpp
40957 views
/*1* Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.2* Copyright (c) 2017, 2020 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#ifndef SHARE_MEMORY_METASPACE_METACHUNK_HPP26#define SHARE_MEMORY_METASPACE_METACHUNK_HPP2728#include "memory/metaspace/chunklevel.hpp"29#include "memory/metaspace/counters.hpp"30#include "utilities/debug.hpp"31#include "utilities/globalDefinitions.hpp"3233class outputStream;3435namespace metaspace {3637class VirtualSpaceNode;3839// A Metachunk is a contiguous metaspace memory region. It is used by40// a MetaspaceArena to allocate from via pointer bump (somewhat similar41// to a TLAB in java heap.42//43// The Metachunk object itself (the "chunk header") is separated from44// the memory region (the chunk payload) it describes. It also can have45// no payload (a "dead" chunk). In itself it lives in C-heap, managed46// as part of a pool of Metachunk headers (ChunkHeaderPool).47//48//49// +---------+ +---------+ +---------+50// |MetaChunk| <--next/prev--> |MetaChunk| <--next/prev--> |MetaChunk| Chunk headers51// +---------+ +---------+ +---------+ in C-heap52// | | |53// base base base54// | / |55// / --------------- /56// / / ----------------------------57// | | /58// v v v59// +---------+ +---------+ +-------------------+60// | | | | | |61// | chunk | | chunk | | chunk | The real chunks ("payload")62// | | | | | | live in Metaspace63// +---------+ +---------+ +-------------------+64//65//66// -- Metachunk state --67//68// A Metachunk is "in-use" if it is part of a MetaspaceArena. That means69// its memory is used - or will be used shortly - to hold VM metadata70// on behalf of a class loader.71//72// A Metachunk is "free" if its payload is currently unused. In that73// case it is managed by a chunk freelist (the ChunkManager).74//75// A Metachunk is "dead" if it does not have a corresponding payload.76// In that case it lives as part of a freelist-of-dead-chunk-headers77// in the ChunkHeaderPool.78//79// A Metachunk is always part of a linked list. In-use chunks are part of80// the chunk list of a MetaspaceArena. Free chunks are in a freelist in81// the ChunkManager. Dead chunk headers are in a linked list as part82// of the ChunkHeaderPool.83//84//85// -- Level --86//87// Metachunks are managed as part of a buddy style allocation scheme.88// Sized always in steps of power-of-2, ranging from the smallest chunk size89// (1Kb) to the largest (4Mb) (see chunklevel.hpp).90// Its size is encoded as level, with level 0 being the largest chunk91// size ("root chunk").92//93//94// -- Payload commit state --95//96// A Metachunk payload (the "real chunk") may be committed, partly committed97// or completely uncommitted. Technically, a payload may be committed98// "checkered" - i.e. committed and uncommitted parts may interleave - but the99// important part is how much contiguous space is committed starting100// at the base of the payload (since that's where we allocate).101//102// The Metachunk keeps track of how much space is committed starting103// at the base of the payload - which is a performace optimization -104// while underlying layers (VirtualSpaceNode->commitmask) keep track105// of the "real" commit state, aka which granules are committed,106// independent on what chunks reside above those granules.107108// +--------------+ <- end -----------+ ----------+109// | | | |110// | | | |111// | | | |112// | | | |113// | | | |114// | ----------- | <- committed_top -- + |115// | | | |116// | | | "free" |117// | | | | size118// | | "free_below_ | |119// | | committed" | |120// | | | |121// | | | |122// | ----------- | <- top --------- + -------- |123// | | | |124// | | "used" | |125// | | | |126// +--------------+ <- start ----------+ ----------+127//128//129// -- Relationships --130//131// Chunks are managed by a binary buddy style allocator132// (see https://en.wikipedia.org/wiki/Buddy_memory_allocation).133// Chunks which are not a root chunk always have an adjoining buddy.134// The first chunk in a buddy pair is called the leader, the second135// one the follower.136//137// +----------+----------+138// | leader | follower |139// +----------+----------+140//141//142// -- Layout in address space --143//144// In order to implement buddy style allocation, we need an easy way to get145// from one chunk to the Metachunk representing the neighboring chunks146// (preceding resp. following it in memory).147// But Metachunk headers and chunks are physically separated, and it is not148// possible to get the Metachunk* from the start of the chunk. Therefore149// Metachunk headers are part of a second linked list, describing the order150// in which their payload appears in memory:151//152// +---------+ +---------+ +---------+153// |MetaChunk| <--next/prev_in_vs--> |MetaChunk| <--next/prev_in_vs--> |MetaChunk|154// +---------+ +---------+ +---------+155// | | |156// base base base157// | / |158// / -------------------------- /159// / / --------------------------------------------------160// | | /161// v v v162// +---------+---------+-------------------+163// | chunk | chunk | chunk |164// +---------+---------+-------------------+165//166167class Metachunk {168169// start of chunk memory; NULL if dead.170MetaWord* _base;171172// Used words.173size_t _used_words;174175// Size of the region, starting from base, which is guaranteed to be committed. In words.176// The actual size of committed regions may actually be larger.177//178// (This is a performance optimization. The underlying VirtualSpaceNode knows179// which granules are committed; but we want to avoid having to ask.)180size_t _committed_words;181182chunklevel_t _level; // aka size.183184// state_free: free, owned by a ChunkManager185// state_in_use: in-use, owned by a MetaspaceArena186// dead: just a hollow chunk header without associated memory, owned187// by chunk header pool.188enum class State : uint8_t {189Free = 0,190InUse = 1,191Dead = 2192};193State _state;194195// We need unfortunately a back link to the virtual space node196// for splitting and merging nodes.197VirtualSpaceNode* _vsnode;198199// A chunk header is kept in a list:200// 1 in the list of used chunks inside a MetaspaceArena, if it is in use201// 2 in the list of free chunks inside a ChunkManager, if it is free202// 3 in the freelist of unused headers inside the ChunkHeaderPool,203// if it is unused (e.g. result of chunk merging) and has no associated204// memory area.205Metachunk* _prev;206Metachunk* _next;207208// Furthermore, we keep, per chunk, information about the neighboring chunks.209// This is needed to split and merge chunks.210//211// Note: These members can be modified concurrently while a chunk is alive and in use.212// This can happen if a neighboring chunk is added or removed.213// This means only read or modify these members under expand lock protection.214Metachunk* _prev_in_vs;215Metachunk* _next_in_vs;216217// Commit uncommitted section of the chunk.218// Fails if we hit a commit limit.219bool commit_up_to(size_t new_committed_words);220221DEBUG_ONLY(static void assert_have_expand_lock();)222223public:224225Metachunk() :226_base(NULL),227_used_words(0),228_committed_words(0),229_level(chunklevel::ROOT_CHUNK_LEVEL),230_state(State::Free),231_vsnode(NULL),232_prev(NULL), _next(NULL),233_prev_in_vs(NULL),234_next_in_vs(NULL)235{}236237void clear() {238_base = NULL;239_used_words = 0; _committed_words = 0;240_level = chunklevel::ROOT_CHUNK_LEVEL;241_state = State::Free;242_vsnode = NULL;243_prev = NULL; _next = NULL;244_prev_in_vs = NULL; _next_in_vs = NULL;245}246247size_t word_size() const { return chunklevel::word_size_for_level(_level); }248249MetaWord* base() const { return _base; }250MetaWord* top() const { return base() + _used_words; }251MetaWord* committed_top() const { return base() + _committed_words; }252MetaWord* end() const { return base() + word_size(); }253254// Chunk list wiring255void set_prev(Metachunk* c) { _prev = c; }256Metachunk* prev() const { return _prev; }257void set_next(Metachunk* c) { _next = c; }258Metachunk* next() const { return _next; }259260DEBUG_ONLY(bool in_list() const { return _prev != NULL || _next != NULL; })261262// Physical neighbors wiring263void set_prev_in_vs(Metachunk* c) { DEBUG_ONLY(assert_have_expand_lock()); _prev_in_vs = c; }264Metachunk* prev_in_vs() const { DEBUG_ONLY(assert_have_expand_lock()); return _prev_in_vs; }265void set_next_in_vs(Metachunk* c) { DEBUG_ONLY(assert_have_expand_lock()); _next_in_vs = c; }266Metachunk* next_in_vs() const { DEBUG_ONLY(assert_have_expand_lock()); return _next_in_vs; }267268bool is_free() const { return _state == State::Free; }269bool is_in_use() const { return _state == State::InUse; }270bool is_dead() const { return _state == State::Dead; }271void set_free() { _state = State::Free; }272void set_in_use() { _state = State::InUse; }273void set_dead() { _state = State::Dead; }274275// Return a single char presentation of the state ('f', 'u', 'd')276char get_state_char() const;277278void inc_level() { _level++; DEBUG_ONLY(chunklevel::is_valid_level(_level);) }279void dec_level() { _level --; DEBUG_ONLY(chunklevel::is_valid_level(_level);) }280chunklevel_t level() const { return _level; }281282// Convenience functions for extreme levels.283bool is_root_chunk() const { return chunklevel::ROOT_CHUNK_LEVEL == _level; }284bool is_leaf_chunk() const { return chunklevel::HIGHEST_CHUNK_LEVEL == _level; }285286VirtualSpaceNode* vsnode() const { return _vsnode; }287288size_t used_words() const { return _used_words; }289size_t free_words() const { return word_size() - used_words(); }290size_t free_below_committed_words() const { return committed_words() - used_words(); }291void reset_used_words() { _used_words = 0; }292293size_t committed_words() const { return _committed_words; }294void set_committed_words(size_t v);295bool is_fully_committed() const { return committed_words() == word_size(); }296bool is_fully_uncommitted() const { return committed_words() == 0; }297298// Ensure that chunk is committed up to at least new_committed_words words.299// Fails if we hit a commit limit.300bool ensure_committed(size_t new_committed_words);301bool ensure_committed_locked(size_t new_committed_words);302303// Ensure that the chunk is committed far enough to serve an additional allocation of word_size.304bool ensure_committed_additional(size_t additional_word_size) {305return ensure_committed(used_words() + additional_word_size);306}307308// Uncommit chunk area. The area must be a common multiple of the309// commit granule size (in other words, we cannot uncommit chunks smaller than310// a commit granule size).311void uncommit();312void uncommit_locked();313314// Allocation from a chunk315316// Allocate word_size words from this chunk (word_size must be aligned to317// allocation_alignment_words).318//319// Caller must make sure the chunk is both large enough and committed far enough320// to hold the allocation. Will always work.321//322MetaWord* allocate(size_t request_word_size);323324// Initialize structure for reuse.325void initialize(VirtualSpaceNode* node, MetaWord* base, chunklevel_t lvl) {326clear();327_vsnode = node; _base = base; _level = lvl;328}329330// Returns true if this chunk is the leader in its buddy pair, false if not.331// Do not call for root chunks.332bool is_leader() const {333assert(!is_root_chunk(), "Root chunks have no buddy."); // Bit harsh?334return is_aligned(base(), chunklevel::word_size_for_level(level() - 1) * BytesPerWord);335}336337//// Debug stuff ////338#ifdef ASSERT339void verify() const;340// Verifies linking with neighbors in virtual space. Needs expand lock protection.341void verify_neighborhood() const;342void zap_header(uint8_t c = 0x17);343344// Returns true if given pointer points into the payload area of this chunk.345bool is_valid_pointer(const MetaWord* p) const {346return base() <= p && p < top();347}348349// Returns true if given pointer points into the commmitted payload area of this chunk.350bool is_valid_committed_pointer(const MetaWord* p) const {351return base() <= p && p < committed_top();352}353354#endif // ASSERT355356void print_on(outputStream* st) const;357358};359360// Little print helpers: since we often print out chunks, here some convenience macros361#define METACHUNK_FORMAT "@" PTR_FORMAT ", %c, base " PTR_FORMAT ", level " CHKLVL_FORMAT362#define METACHUNK_FORMAT_ARGS(chunk) p2i(chunk), chunk->get_state_char(), p2i(chunk->base()), chunk->level()363364#define METACHUNK_FULL_FORMAT "@" PTR_FORMAT ", %c, base " PTR_FORMAT ", level " CHKLVL_FORMAT " (" SIZE_FORMAT "), used: " SIZE_FORMAT ", committed: " SIZE_FORMAT ", committed-free: " SIZE_FORMAT365#define METACHUNK_FULL_FORMAT_ARGS(chunk) p2i(chunk), chunk->get_state_char(), p2i(chunk->base()), chunk->level(), chunk->word_size(), chunk->used_words(), chunk->committed_words(), chunk->free_below_committed_words()366367} // namespace metaspace368369#endif // SHARE_MEMORY_METASPACE_METACHUNK_HPP370371372