/* * Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2017, 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. *
*/
// A Metachunk is a contiguous metaspace memory region. It is used by // a MetaspaceArena to allocate from via pointer bump (somewhat similar // to a TLAB in java heap. // // The Metachunk object itself (the "chunk header") is separated from // the memory region (the chunk payload) it describes. It also can have // no payload (a "dead" chunk). In itself it lives in C-heap, managed // as part of a pool of Metachunk headers (ChunkHeaderPool). // // // +---------+ +---------+ +---------+ // |MetaChunk| <--next/prev--> |MetaChunk| <--next/prev--> |MetaChunk| Chunk headers // +---------+ +---------+ +---------+ in C-heap // | | | // base base base // | / | // / --------------- / // / / ---------------------------- // | | / // v v v // +---------+ +---------+ +-------------------+ // | | | | | | // | chunk | | chunk | | chunk | The real chunks ("payload") // | | | | | | live in Metaspace // +---------+ +---------+ +-------------------+ // // // -- Metachunk state -- // // A Metachunk is "in-use" if it is part of a MetaspaceArena. That means // its memory is used - or will be used shortly - to hold VM metadata // on behalf of a class loader. // // A Metachunk is "free" if its payload is currently unused. In that // case it is managed by a chunk freelist (the ChunkManager). // // A Metachunk is "dead" if it does not have a corresponding payload. // In that case it lives as part of a freelist-of-dead-chunk-headers // in the ChunkHeaderPool. // // A Metachunk is always part of a linked list. In-use chunks are part of // the chunk list of a MetaspaceArena. Free chunks are in a freelist in // the ChunkManager. Dead chunk headers are in a linked list as part // of the ChunkHeaderPool. // // // -- Level -- // // Metachunks are managed as part of a buddy style allocation scheme. // Sized always in steps of power-of-2, ranging from the smallest chunk size // (1Kb) to the largest (4Mb) (see chunklevel.hpp). // Its size is encoded as level, with level 0 being the largest chunk // size ("root chunk"). // // // -- Payload commit state -- // // A Metachunk payload (the "real chunk") may be committed, partly committed // or completely uncommitted. Technically, a payload may be committed // "checkered" - i.e. committed and uncommitted parts may interleave - but the // important part is how much contiguous space is committed starting // at the base of the payload (since that's where we allocate). // // The Metachunk keeps track of how much space is committed starting // at the base of the payload - which is a performance optimization - // while underlying layers (VirtualSpaceNode->commitmask) keep track // of the "real" commit state, aka which granules are committed, // independent on what chunks reside above those granules.
// +--------------+ <- end -----------+ ----------+ // | | | | // | | | | // | | | | // | | | | // | | | | // | ----------- | <- committed_top -- + | // | | | | // | | | "free" | // | | | | size // | | "free_below_ | | // | | committed" | | // | | | | // | | | | // | ----------- | <- top --------- + -------- | // | | | | // | | "used" | | // | | | | // +--------------+ <- start ----------+ ----------+ // // // -- Relationships -- // // Chunks are managed by a binary buddy style allocator // (see https://en.wikipedia.org/wiki/Buddy_memory_allocation). // Chunks which are not a root chunk always have an adjoining buddy. // The first chunk in a buddy pair is called the leader, the second // one the follower. // // +----------+----------+ // | leader | follower | // +----------+----------+ // // // -- Layout in address space -- // // In order to implement buddy style allocation, we need an easy way to get // from one chunk to the Metachunk representing the neighboring chunks // (preceding resp. following it in memory). // But Metachunk headers and chunks are physically separated, and it is not // possible to get the Metachunk* from the start of the chunk. Therefore // Metachunk headers are part of a second linked list, describing the order // in which their payload appears in memory: // // +---------+ +---------+ +---------+ // |MetaChunk| <--next/prev_in_vs--> |MetaChunk| <--next/prev_in_vs--> |MetaChunk| // +---------+ +---------+ +---------+ // | | | // base base base // | / | // / -------------------------- / // / / -------------------------------------------------- // | | / // v v v // +---------+---------+-------------------+ // | chunk | chunk | chunk | // +---------+---------+-------------------+ //
class Metachunk {
// start of chunk memory; NULL if dead.
MetaWord* _base;
// Used words.
size_t _used_words;
// Size of the region, starting from base, which is guaranteed to be committed. In words. // The actual size of committed regions may actually be larger. // // (This is a performance optimization. The underlying VirtualSpaceNode knows // which granules are committed; but we want to avoid having to ask.)
size_t _committed_words;
chunklevel_t _level; // aka size.
// state_free: free, owned by a ChunkManager // state_in_use: in-use, owned by a MetaspaceArena // dead: just a hollow chunk header without associated memory, owned // by chunk header pool. enumclass State : uint8_t {
Free = 0,
InUse = 1,
Dead = 2
};
State _state;
// We need unfortunately a back link to the virtual space node // for splitting and merging nodes.
VirtualSpaceNode* _vsnode;
// A chunk header is kept in a list: // 1 in the list of used chunks inside a MetaspaceArena, if it is in use // 2 in the list of free chunks inside a ChunkManager, if it is free // 3 in the freelist of unused headers inside the ChunkHeaderPool, // if it is unused (e.g. result of chunk merging) and has no associated // memory area.
Metachunk* _prev;
Metachunk* _next;
// Furthermore, we keep, per chunk, information about the neighboring chunks. // This is needed to split and merge chunks. // // Note: These members can be modified concurrently while a chunk is alive and in use. // This can happen if a neighboring chunk is added or removed. // This means only read or modify these members under expand lock protection.
Metachunk* _prev_in_vs;
Metachunk* _next_in_vs;
// Commit uncommitted section of the chunk. // Fails if we hit a commit limit. bool commit_up_to(size_t new_committed_words);
// Ensure that chunk is committed up to at least new_committed_words words. // Fails if we hit a commit limit. bool ensure_committed(size_t new_committed_words); bool ensure_committed_locked(size_t new_committed_words);
// Ensure that the chunk is committed far enough to serve an additional allocation of word_size. bool ensure_committed_additional(size_t additional_word_size) { return ensure_committed(used_words() + additional_word_size);
}
// Uncommit chunk area. The area must be a common multiple of the // commit granule size (in other words, we cannot uncommit chunks smaller than // a commit granule size). void uncommit(); void uncommit_locked();
// Allocation from a chunk
// Allocate word_size words from this chunk (word_size must be aligned to // allocation_alignment_words). // // Caller must make sure the chunk is both large enough and committed far enough // to hold the allocation. Will always work. //
MetaWord* allocate(size_t request_word_size);
// Returns true if this chunk is the leader in its buddy pair, false if not. // Do not call for root chunks. bool is_leader() const {
assert(!is_root_chunk(), "Root chunks have no buddy."); // Bit harsh? return is_aligned(base(), chunklevel::word_size_for_level(level() - 1) * BytesPerWord);
}
//// Debug stuff //// #ifdef ASSERT void verify() const; // Verifies linking with neighbors in virtual space. Needs expand lock protection. void verify_neighborhood() const; void zap_header(uint8_t c = 0x17);
// Returns true if given pointer points into the payload area of this chunk. bool is_valid_pointer(const MetaWord* p) const { return base() <= p && p < top();
}
// Returns true if given pointer points into the committed payload area of this chunk. bool is_valid_committed_pointer(const MetaWord* p) const { return base() <= p && p < committed_top();
}
#endif// ASSERT
void print_on(outputStream* st) const;
};
// Little print helpers: since we often print out chunks, here some convenience macros #define METACHUNK_FORMAT "@" PTR_FORMAT ", %c, base " PTR_FORMAT ", level " CHKLVL_FORMAT #define METACHUNK_FORMAT_ARGS(chunk) p2i(chunk), chunk->get_state_char(), p2i(chunk->base()), chunk->level()
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.