--- /dev/null 2019-07-25 13:07:35.456009699 +0200 +++ new/src/hotspot/share/memory/metaspace/chunkTree.hpp 2019-07-25 15:30:05.177457970 +0200 @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2018, 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_CHUNKTREE_HPP +#define SHARE_MEMORY_METASPACE_CHUNKTREE_HPP + +#include "memory/metaspace/abstractPool.hpp" +#include "memory/metaspace/chunkLevel.hpp" +#include "memory/metaspace/metachunk.hpp" + +namespace metaspace { + +// Chunks live in a binary tree. +// + +class ChunkClosure { + public: + // Return false to cancel traversal. + virtual bool do_chunk(Metachunk* chunk) = 0; +}; + + +class ChunkTree { + + typedef u2 ref_t; + + // Root is either a direct pointer to a Metachunk* (in that case, a root chunk of max. size) + // or a pointer to a node. + ref_t _root; + + struct btnode_t { + + ref_t parent; + ref_t child[2]; + + }; + + typedef AbstractPool NodePoolType; + typedef AbstractPool ChunkPoolType; + NodePoolType _nodePool; + ChunkPoolType _chunkPool; + + // The upper two bits of a reference encode information about it. + // bit 0,1: 00 - reference is a btnode_t + // 10 - reference is a free chunk + // 11 - reference is a chunk in use. + // This also means a reference has to get by with 14 bits. Which covers 16K, which is enough for both + // chunk headers and nodes within one root chunk area. + static const u2 highest_possible_index = (1 << 14) - 1; + static const u2 node_marker = 0; + static const u2 free_chunk_marker = 2; + static const u2 used_chunk_marker = 3; + + static u2 get_raw_index_from_reference(ref_t ref) { return 0x3FFF & ref; } + static u2 get_info_from_reference(ref_t ref) { return 0xc000 & ref; } + + static u2 encode_reference(u2 raw_idx, u2 info) { + assert(raw_idx <= highest_possible_index, "invalid index"); + return (info << 14) | raw_idx; + } + +#ifdef ASSERT + static bool reference_is_node(ref_t ref) { return get_info_from_reference(ref) == node_marker; } + static bool reference_is_chunk(ref_t ref) { u2 i = get_info_from_reference(ref); return i == free_chunk_marker || i == used_chunk_marker; } + static bool reference_is_used_chunk(ref_t ref) { return get_info_from_reference(ref) == used_chunk_marker; } + + void check_is_valid_node_ref(ref_t ref) { assert(resolve_reference_to_node(ref) != NULL, "invalid node ref"); } + void check_is_valid_chunk_ref(ref_t ref) { assert(resolve_reference_to_chunk(ref) != NULL, "invalid chunk ref"); } + void check_is_valid_ref(ref_t ref); +#endif + + static bool reference_is_free_chunk(ref_t ref) { return get_info_from_reference(ref) == free_chunk_marker; } + + // Given a reference we know to be a node, resolve it to the node pointer. + btnode_t* resolve_reference_to_node(ref_t ref) const { + assert(reference_is_node(ref), "Not a node ref"); + return _nodePool.elem_at_index(get_raw_index_from_reference(ref)); + } + + // Allocate a new node. Node is uninitialized. + // Returns pointer to node, and reference in ref. + btnode_t* allocate_new_node() { + return _nodePool.allocate_element(); + } + + // Given a node pointer, return its correctly encoded reference. + ref_t encode_reference_for_node(const btnode_t* n) const { + const u2 raw_idx = _nodePool.index_for_elem(n); + return encode_reference(raw_idx, node_marker); + } + + // Release a node to the pool. + void release_node(btnode_t* n) { + _nodePool.return_element(n); + } + + // Given a reference we know to be a chunk, resolve it to the chunk pointer. + Metachunk* resolve_reference_to_chunk(ref_t ref) const { + assert(reference_is_chunk(ref), "Not a chunk ref"); + return _chunkPool.elem_at_index(get_raw_index_from_reference(ref)); + } + + // Allocate a new node. Node is uninitialized. + // Returns pointer to node, and reference in ref. + Metachunk* allocate_new_chunk() { + return _chunkPool.allocate_element(); + } + + // Given a chunk pointer, return its correctly encoded reference. + ref_t encode_reference_for_chunk(Metachunk* c, bool is_free) const { + const u2 raw_idx = _chunkPool.index_for_elem(c); + return encode_reference(raw_idx, is_free ? free_chunk_marker : used_chunk_marker); + } + + // Release a chunk to the pool. + void release_chunk(Metachunk* c) { + _chunkPool.return_element(c); + } + + //// Helpers for tree traversal //// + + class ConstChunkClosure; + bool iterate_chunks_helper(ref_t ref, ChunkClosure* cc) const; + +#ifdef ASSERT + // Verify a life node (one which lives in the tree). + void verify_node(const btnode_t* n) const; + // Helper for verify() + void verify_helper(bool slow, ref_t ref, const MetaWord* p, int* num_chunks, int* num_nodes) const; +#endif + + // Given a chunk c, split it once. + // + // The original chunk must not be part of a freelist. + // + // Returns pointer to the result chunk; updates the splinters array to return the splintered off chunk. + // + // Returns NULL if chunk cannot be split any further. + Metachunk* split_once(Metachunk* c, Metachunk* splinters[chklvl::NUM_CHUNK_LEVELS]); + + // Given a chunk, attempt to merge it with its sibling if it is free. + // Returns pointer to the result chunk if successful, NULL otherwise. + // + // Returns number of merged chunks, by chunk level, in num_merged array. These numbers + // includes the original chunk. + // + // !!! Please note that if this method returns a non-NULL value, the + // original chunk will be invalid and should not be accessed anymore! !!! + Metachunk* merge_once(Metachunk* c, int num_merged[chklvl::NUM_CHUNK_LEVELS]); + +public: + + ChunkTree(); + + // Initialize: allocate a root node and a root chunk header; return the + // root chunk header. It will be partly initialized. + // Note: this just allocates a memory-less header; memory itself is allocated inside VirtualSpaceNode. + Metachunk* alloc_root_chunk_header(); + + // Given a chunk c, split it recursively until you get a chunk of the given target_level. + // + // The original chunk must not be part of a freelist. + // + // Returns pointer to the result chunk; returns split off chunks in splinters array. + // + // Returns NULL if chunk cannot be split at least once. + Metachunk* split(chklvl_t target_level, Metachunk* c, Metachunk* splinters[chklvl::NUM_CHUNK_LEVELS]); + + // Given a chunk, attempt to merge it recursively with its neighboring chunks. + // + // If successful (merged at least once), returns address of + // the merged chunk; NULL otherwise. + // + // The merged chunks are removed from their freelist; the number of merged chunks is + // returned, split by level, in num_merged array. Note that these numbers does not + // include the original chunk. + // + // !!! Please note that if this method returns a non-NULL value, the + // original chunk will be invalid and should not be accessed anymore! !!! + Metachunk* merge(Metachunk* c, int num_merged[chklvl::NUM_CHUNK_LEVELS]); + + //// tree traversal //// + + // Iterate over all nodes in this tree. Returns true for complete traversal, + // false if traversal was cancelled. + bool iterate_chunks(ChunkClosure* cc) const; + + + //// Debug stuff //// + + // Verify tree. If base != NULL, it should point to the location assumed + // to be base of the first chunk. + DEBUG_ONLY(void verify(bool slow, const MetaWord* base) const;) + + // Returns the footprint of this tree, in words. + size_t memory_footprint_words() const; + + +}; + + +/////////////////////// +// An C-heap allocated array of chunk trees. Used to describe fragmentation over a range of multiple root chunks. +class ChunkTreeArray { + + const MetaWord* const _base; + const size_t _word_size; + + ChunkTree** _arr; + int _num; + +#ifdef ASSERT + void check_pointer(const MetaWord* p) const { + assert(p >= _base && p < _base + _word_size, "Invalid pointer"); + } +#endif + + int index_by_address(const MetaWord* p) const { + DEBUG_ONLY(check_pointer(p);) + return (p - _base) / chklvl::MAX_CHUNK_WORD_SIZE; + } + +public: + + // Create an array of ChunkTree objects, all initialized to NULL, covering + // a given memory range. Memory range must be aligned to size of root chunks. + ChunkTreeArray(const MetaWord* base, size_t word_size); + + ~ChunkTreeArray(); + + // Given a memory address into the range the trees cover, return the corresponding + // tree. If none existed at this position, create it. + ChunkTree* get_tree_by_address(const MetaWord* p) const { + assert(p >= _base && p < _base + _word_size, "Invalid pointer"); + const int idx = index_by_address(p); + assert(idx >= 0 && idx < _num, "Invalid index"); + if (_arr[idx] == NULL) { + _arr[idx] = new ChunkTree(); + } + return _arr[idx]; + } + + // Iterate over all nodes in all trees. Returns true for complete traversal, + // false if traversal was cancelled. + bool iterate_chunks(ChunkClosure* cc) const; + + DEBUG_ONLY(void verify(bool slow) const;) + + // Returns the footprint of all trees in this array, in words. + size_t memory_footprint_words() const; + +}; + + +} // namespace metaspace + +#endif // SHARE_MEMORY_METASPACE_CHUNKTREE_HPP