--- old/src/hotspot/share/memory/metaspace/metachunk.hpp 2019-07-25 15:29:46.053187277 +0200 +++ new/src/hotspot/share/memory/metaspace/metachunk.hpp 2019-07-25 15:29:45.833184153 +0200 @@ -24,12 +24,14 @@ #ifndef SHARE_MEMORY_METASPACE_METACHUNK_HPP #define SHARE_MEMORY_METASPACE_METACHUNK_HPP -#include "memory/metaspace/metabase.hpp" -#include "memory/metaspace/metaspaceCommon.hpp" + +#include "memory/metaspace/counter.hpp" +#include "memory/metaspace/abstractPool.hpp" +#include "memory/metaspace/chunkLevel.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" -class MetachunkTest; + namespace metaspace { @@ -39,135 +41,196 @@ // 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 { - - 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; } +// +--------------+ <- 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; + } - 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. + //// Debug stuff //// + DEBUG_ONLY(void verify(bool slow) const;) - // 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; } +class MetachunkList { - int get_use_count() const { return _use_count; } - void inc_use_count() { _use_count ++; } + Metachunk* _first; + IntCounter _num; - ChunkOrigin get_origin() const { return _origin; } - void set_origin(ChunkOrigin orig) { _origin = orig; } +public: - ChunkIndex get_chunk_type() const { return _chunk_type; } - bool is_class() const { return _is_class; } + MetachunkList() : _first(NULL), _num() {} - DEBUG_ONLY(void mangle(juint word_value);) - DEBUG_ONLY(void verify() const;) + 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 }; - -// 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