< prev index next >
src/hotspot/share/memory/metaspace/metachunk.hpp
Print this page
rev 60538 : imported patch jep387-all.patch
*** 1,7 ****
/*
! * Copyright (c) 2012, 2019, Oracle and/or its affiliates. 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.
--- 1,8 ----
/*
! * Copyright (c) 2012, 2020, 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.
*** 19,173 ****
* 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.
*
*/
#ifndef SHARE_MEMORY_METASPACE_METACHUNK_HPP
#define SHARE_MEMORY_METASPACE_METACHUNK_HPP
! #include "memory/metaspace/metabase.hpp"
! #include "memory/metaspace/metaspaceCommon.hpp"
#include "utilities/debug.hpp"
#include "utilities/globalDefinitions.hpp"
! class MetachunkTest;
namespace metaspace {
class VirtualSpaceNode;
! // Metachunk - Quantum of allocation from a Virtualspace
! // Metachunks are reused (when freed are put on a global freelist) and
! // have no permanent association to a SpaceManager.
! // +--------------+ <- end --+ --+
// | | | |
- // | | | free |
// | | | |
- // | | | | size | capacity
// | | | |
- // | | <- top -- + |
// | | | |
- // | | | used |
// | | | |
// | | | |
! // +--------------+ <- bottom --+ --+
!
! enum ChunkOrigin {
! // Chunk normally born (via take_from_committed)
! origin_normal = 1,
! // Chunk was born as padding chunk
! origin_pad = 2,
! // Chunk was born as leftover chunk in VirtualSpaceNode::retire
! origin_leftover = 3,
! // Chunk was born as result of a merge of smaller chunks
! origin_merge = 4,
! // Chunk was born as result of a split of a larger chunk
! origin_split = 5,
!
! origin_minimum = origin_normal,
! origin_maximum = origin_split,
! origins_count = origin_maximum + 1
! };
! inline bool is_valid_chunkorigin(ChunkOrigin origin) {
! return origin == origin_normal ||
! origin == origin_pad ||
! origin == origin_leftover ||
! origin == origin_merge ||
! origin == origin_split;
! }
!
! class Metachunk : public Metabase<Metachunk> {
!
! friend class ::MetachunkTest;
!
! // The VirtualSpaceNode containing this chunk.
! VirtualSpaceNode* const _container;
!
! // Current allocation top.
! MetaWord* _top;
!
! // A 32bit sentinel for debugging purposes.
! enum { CHUNK_SENTINEL = 0x4d4554EF, // "MET"
! CHUNK_SENTINEL_INVALID = 0xFEEEEEEF
};
! uint32_t _sentinel;
!
! const ChunkIndex _chunk_type;
! const bool _is_class;
! // Whether the chunk is free (in freelist) or in use by some class loader.
! bool _is_tagged_free;
!
! ChunkOrigin _origin;
! int _use_count;
!
! MetaWord* initial_top() const { return (MetaWord*)this + overhead(); }
! MetaWord* top() const { return _top; }
!
! public:
! // Metachunks are allocated out of a MetadataVirtualSpace and
! // and use some of its space to describe itself (plus alignment
! // considerations). Metadata is allocated in the rest of the chunk.
! // This size is the overhead of maintaining the Metachunk within
! // the space.
! // Alignment of each allocation in the chunks.
! static size_t object_alignment();
!
! // Size of the Metachunk header, in words, including alignment.
! static size_t overhead();
!
! Metachunk(ChunkIndex chunktype, bool is_class, size_t word_size, VirtualSpaceNode* container);
!
! MetaWord* allocate(size_t word_size);
!
! VirtualSpaceNode* container() const { return _container; }
!
! MetaWord* bottom() const { return (MetaWord*) this; }
!
! // Reset top to bottom so chunk can be reused.
! void reset_empty() { _top = initial_top(); clear_next(); clear_prev(); }
! bool is_empty() { return _top == initial_top(); }
!
! // used (has been allocated)
! // free (available for future allocations)
! size_t word_size() const { return size(); }
! size_t used_word_size() const;
! size_t free_word_size() const;
!
! bool is_tagged_free() { return _is_tagged_free; }
! void set_is_tagged_free(bool v) { _is_tagged_free = v; }
!
! bool contains(const void* ptr) { return bottom() <= ptr && ptr < _top; }
void print_on(outputStream* st) const;
- bool is_valid_sentinel() const { return _sentinel == CHUNK_SENTINEL; }
- void remove_sentinel() { _sentinel = CHUNK_SENTINEL_INVALID; }
-
- int get_use_count() const { return _use_count; }
- void inc_use_count() { _use_count ++; }
-
- ChunkOrigin get_origin() const { return _origin; }
- void set_origin(ChunkOrigin orig) { _origin = orig; }
-
- ChunkIndex get_chunk_type() const { return _chunk_type; }
- bool is_class() const { return _is_class; }
-
- DEBUG_ONLY(void mangle(juint word_value);)
- DEBUG_ONLY(void verify() const;)
-
};
! // Helper function that does a bunch of checks for a chunk.
! DEBUG_ONLY(void do_verify_chunk(Metachunk* chunk);)
!
! // Given a Metachunk, update its in-use information (both in the
! // chunk and the occupancy map).
! void do_update_in_use_info_for_chunk(Metachunk* chunk, bool inuse);
} // namespace metaspace
#endif // SHARE_MEMORY_METASPACE_METACHUNK_HPP
--- 20,321 ----
* 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.
*
*/
+
#ifndef SHARE_MEMORY_METASPACE_METACHUNK_HPP
#define SHARE_MEMORY_METASPACE_METACHUNK_HPP
!
! #include "memory/metaspace/counter.hpp"
! #include "memory/metaspace/chunkLevel.hpp"
#include "utilities/debug.hpp"
#include "utilities/globalDefinitions.hpp"
!
! class outputStream;
namespace metaspace {
class VirtualSpaceNode;
! // A Metachunk is a contiguous metaspace memory region. It is part of
! // a MetaspaceArena, which keeps a list of MetaChunk and allocates via
! // pointer bump from the top element in the list.
! //
! // 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 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.
! //
! // -- 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 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 performace 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 ----------+ ----------+
! // Note: this is a chunk **descriptor**. The real Payload area lives in metaspace,
! // this class lives somewhere else.
! 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.
! enum state_t {
! state_free = 0,
! state_in_use = 1,
! state_dead = 2
};
+ state_t _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);
!
! DEBUG_ONLY(static void assert_have_expand_lock();)
!
! public:
!
! Metachunk()
! : _base(NULL),
! _used_words(0),
! _committed_words(0),
! _level(chunklevel::ROOT_CHUNK_LEVEL),
! _state(state_free),
! _vsnode(NULL),
! _prev(NULL), _next(NULL),
! _prev_in_vs(NULL), _next_in_vs(NULL)
! {}
!
! void clear() {
! _base = NULL;
! _used_words = 0;
! _committed_words = 0;
! _level = chunklevel::ROOT_CHUNK_LEVEL;
! _state = state_free;
! _vsnode = NULL;
! _prev = NULL;
! _next = NULL;
! _prev_in_vs = NULL;
! _next_in_vs = NULL;
! }
!
!
! size_t word_size() const { return chunklevel::word_size_for_level(_level); }
!
! MetaWord* base() const { return _base; }
! MetaWord* top() const { return base() + _used_words; }
! MetaWord* committed_top() const { return base() + _committed_words; }
! MetaWord* end() const { return base() + word_size(); }
!
! // Chunk list wiring
! void set_prev(Metachunk* c) { _prev = c; }
! Metachunk* prev() const { return _prev; }
! void set_next(Metachunk* c) { _next = c; }
! Metachunk* next() const { return _next; }
!
! DEBUG_ONLY(bool in_list() const { return _prev != NULL || _next != NULL; })
!
! // Physical neighbors wiring
! void set_prev_in_vs(Metachunk* c) { DEBUG_ONLY(assert_have_expand_lock()); _prev_in_vs = c; }
! Metachunk* prev_in_vs() const { DEBUG_ONLY(assert_have_expand_lock()); return _prev_in_vs; }
! void set_next_in_vs(Metachunk* c) { DEBUG_ONLY(assert_have_expand_lock()); _next_in_vs = c; }
! Metachunk* next_in_vs() const { DEBUG_ONLY(assert_have_expand_lock()); return _next_in_vs; }
!
! bool is_free() const { return _state == state_free; }
! bool is_in_use() const { return _state == state_in_use; }
! bool is_dead() const { return _state == state_dead; }
! void set_free() { _state = state_free; }
! void set_in_use() { _state = state_in_use; }
! void set_dead() { _state = state_dead; }
!
! // Return a single char presentation of the state ('f', 'u', 'd')
! char get_state_char() const;
!
! void inc_level() { _level ++; DEBUG_ONLY(chunklevel::is_valid_level(_level);) }
! void dec_level() { _level --; DEBUG_ONLY(chunklevel::is_valid_level(_level);) }
! chunklevel_t level() const { return _level; }
!
! // Convenience functions for extreme levels.
! bool is_root_chunk() const { return chunklevel::ROOT_CHUNK_LEVEL == _level; }
! bool is_leaf_chunk() const { return chunklevel::HIGHEST_CHUNK_LEVEL == _level; }
!
! VirtualSpaceNode* vsnode() const { return _vsnode; }
!
! size_t used_words() const { return _used_words; }
! size_t free_words() const { return word_size() - used_words(); }
! size_t free_below_committed_words() const { return committed_words() - used_words(); }
! void reset_used_words() { _used_words = 0; }
!
! size_t committed_words() const { return _committed_words; }
! void set_committed_words(size_t v);
! bool is_fully_committed() const { return committed_words() == word_size(); }
! bool is_fully_uncommitted() const { return committed_words() == 0; }
!
! // 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);
!
! bool ensure_fully_committed() { return ensure_committed(word_size()); }
! bool ensure_fully_committed_locked() { return ensure_committed_locked(word_size()); }
!
! // 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);
!
! // Initialize structure for reuse.
! void initialize(VirtualSpaceNode* node, MetaWord* base, chunklevel_t lvl) {
! _vsnode = node; _base = base; _level = lvl;
! _used_words = _committed_words = 0; _state = state_free;
! _next = _prev = _next_in_vs = _prev_in_vs = NULL;
! }
!
! // 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(bool slow) const;
! // Verifies linking with neighbors in virtual space. Needs expand lock protection.
! void verify_neighborhood() const;
! void zap_header(uint8_t c = 0x17);
! void fill_with_pattern(MetaWord pattern, size_t word_size);
! void check_pattern(MetaWord pattern, size_t word_size);
!
! // 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 commmitted 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()
! #define METACHUNK_FULL_FORMAT "@" PTR_FORMAT ", %c, base " PTR_FORMAT ", level " CHKLVL_FORMAT " (" SIZE_FORMAT "), used: " SIZE_FORMAT ", committed: " SIZE_FORMAT ", committed-free: " SIZE_FORMAT
! #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()
} // namespace metaspace
#endif // SHARE_MEMORY_METASPACE_METACHUNK_HPP
< prev index next >