/* * 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. * * 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. * */ #ifndef SHARE_MEMORY_METASPACE_METACHUNK_HPP #define SHARE_MEMORY_METASPACE_METACHUNK_HPP #include "memory/metaspace/counter.hpp" #include "memory/metaspace/abstractPool.hpp" #include "memory/metaspace/chunkLevel.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" 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 (aka capacity) // | | | | // | ----------- | <- top -- + | // | | | | // | | | used | // +--------------+ <- start -- + -- + // Note: this is a chunk **descriptor**. The real Payload area lives in metaspace, // this class lives somewhere else. class Metachunk { // Todo: compact this node. A lot of things can be expressed more tighter. // A chunk header is kept in a list: // - in the list of used chunks inside a SpaceManager, if it is in use // - in the list of free chunks inside a ChunkManager, if it is free // - in the freelist of dead headers inside the MetaChunkHeaderPool, // if it is dead (e.g. result of chunk merging). Metachunk* _prev; Metachunk* _next; chklvl_t _level; // aka size. // true: free, owned by ChunkManager // false: in-use, owned by SpaceManager // if dead, meaningless bool _is_free; // start of chunk memory; NULL if dead. MetaWord* _base; // Used words. size_t _used_words; // Guaranteed-to-be-committed-words, counted from base // (This is a performance optimization. The underlying VirtualSpaceNode knows // which granules are committed; but we want to avoid asking it unnecessarily // in Metachunk::allocate(), so we keep a limit until which we are guaranteed // to have committed memory under us.) size_t _committed_words; // the chunk tree node this header is hanging under; NULL if dead. u2 _tree_node_ref; // We need unfortunately a back link to the virtual space node // for splitting and merging nodes. VirtualSpaceNode* _vsnode; MetaWord* top() const { return base() + _used_words; } public: Metachunk() : _prev(NULL), _next(NULL), _level(chklvl::ROOT_CHUNK_LEVEL), _is_free(true), _base(NULL), _used_words(0), _committed_words(0), _tree_node_ref(0), _vsnode(NULL) {} size_t word_size() const { return chklvl::word_size_for_level(_level); } MetaWord* base() const { return _base; } void set_base(MetaWord* p) { _base = p; } MetaWord* end() const { return base() + word_size(); } void set_prev(Metachunk* c) { _prev = c; } Metachunk* prev() const { return _prev; } void set_next(Metachunk* c) { _next = c; } Metachunk* next() const { return _next; } // Remove chunk from whatever list it lives in by wiring next with previous. void remove_from_list(); bool is_free() const { return _is_free; } bool is_in_use() const { return !_is_free; } void set_free(bool v) { _is_free = v; } void inc_level() { _level ++; DEBUG_ONLY(chklvl::is_valid_level(_level);) } void dec_level() { _level --; DEBUG_ONLY(chklvl::is_valid_level(_level);) } void set_level(chklvl_t v) { _level = v; DEBUG_ONLY(chklvl::is_valid_level(_level);) } chklvl_t level() const { return _level; } void set_tree_node_ref(u2 v) { _tree_node_ref = v; } u2 tree_node_ref() const { return _tree_node_ref; } VirtualSpaceNode* vsnode() const { return _vsnode; } void set_vsnode(VirtualSpaceNode* n) { _vsnode = n; } 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) { _committed_words = v; } bool is_fully_committed() const { return committed_words() == word_size(); } // Ensure that chunk is committed up to at least word_size words. // Fails if we hit a commit limit. bool ensure_committed(size_t word_size); // Alignment of an allocation. static const size_t allocation_alignment_bytes = 8; static const size_t allocation_alignment_words = allocation_alignment_bytes / BytesPerWord; // Allocation from a chunk // Allocate word_size words from this chunk. // // May cause memory to be committed. That may fail if we hit a commit limit. In that case, // NULL is returned and p_did_hit_commit_limit will be set to true. // If the remainder portion of the chunk was too small to hold the allocation, // NULL is returned and p_did_hit_commit_limit will be set to false. MetaWord* allocate(size_t word_size, bool* p_did_hit_commit_limit); // Wipe this object to look as if it were default constructed. void wipe() { _prev = NULL; _next = NULL; _level = chklvl::ROOT_CHUNK_LEVEL; _is_free = true; _base = NULL; _used_words = 0; _committed_words = 0; _tree_node_ref = 0; _vsnode = NULL; } //// Debug stuff //// DEBUG_ONLY(void verify(bool slow) const;) }; class MetachunkList { Metachunk* _first; IntCounter _num; public: MetachunkList() : _first(NULL), _num() {} Metachunk* first() const { return _first; } int size() const { return _num.get(); } void add(Metachunk* c) { c->set_next(_first); _first = c; _num.increment(); } // Remove first node unless empty. Returns node or NULL. Metachunk* remove_first() { Metachunk* c = _first; if (c != NULL) { _first = c->next(); _num.decrement(); } return c; } // Remove given chunk from list. List must contain that chunk. void remove(Metachunk* c) { assert(contains(c), "Does not contain this chunk"); c->remove_from_list(); _num.decrement(); } // Manually decrement counter; needed for cases where chunks // have been manually removed from the list without informing // the list, e.g. chunk merging, see chunkManager::return_chunk(). void dec_counter_by(int v) { _num.decrement_by(v); } #ifdef ASSERT bool contains(const Metachunk* c) const; void verify(bool slow) const; #endif }; } // namespace metaspace #endif // SHARE_MEMORY_METASPACE_METACHUNK_HPP