1 /* 2 * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 #ifndef SHARE_MEMORY_METASPACE_CHUNKTREE_HPP 26 #define SHARE_MEMORY_METASPACE_CHUNKTREE_HPP 27 28 #include "memory/metaspace/abstractPool.hpp" 29 #include "memory/metaspace/chunkLevel.hpp" 30 #include "memory/metaspace/metachunk.hpp" // For MetachunkPool 31 32 namespace metaspace { 33 34 // Chunks live in a quaternary tree. 35 // 36 // Each node has four child nodes. 37 // Leaf nodes are the chunk headers. 38 39 struct qtnode_t; 40 41 struct qtnode_t { 42 43 // parent node; NULL if this is a root node. 44 qtnode_t* parent; 45 46 // Child nodes can be either non-leaf itself, in which case they are qtnode_t*; 47 // or they are leaf nodes, in which case they are Metachunk*. 48 void* child[4]; 49 50 // For the purpose of keeping this structure in a free list of the 51 // ChunkTreeNodePool, reuse the parent pointer as next (this only 52 // happens to dead nodes, so the parent pointer is not needed). 53 // See AbstractPool<> class. 54 qtnode_t* next() { return parent; } 55 void set_next(qtnode_t* n) { parent = n; } 56 57 #ifdef ASSERT 58 bool check_is_child(const void* p) const { 59 return child[0] == p || child[1] == p || child[2] == p || child[3] == p; 60 } 61 #endif 62 63 }; 64 65 typedef AbstractPool<qtnode_t, uint32_t> ChunkTreeNodePool; 66 67 class ChunkTree { 68 69 // Root is either a direct pointer to a Metachunk* (in that case, a root chunk of max. size) 70 // or a pointer to a node. 71 void* _root; 72 73 /// Node allocation /// 74 75 static ChunkTreeNodePool _nodePool; 76 77 // Returns a new node. Node will be zero'd out completely. 78 static qtnode_t* allocate_node() { 79 qtnode_t* n = _nodePool.allocate_element(); 80 memset(n, 0, sizeof(n)); 81 return n; 82 } 83 84 static qtnode_t* release_node(qtnode_t* n) { 85 return _nodePool.return_element(n); 86 } 87 88 // Given a pointer to a child which can either be a Metachunk or a node, return a pointer to a 89 // node if it was a node; NULL if it was a Metachunk. 90 static qtnode_t* resolve_node_ptr(void* p) { 91 qtnode_t* n = (qtnode_t*)p; 92 if (_nodePool.is_valid_elem(n)) { 93 return n; 94 } 95 return NULL; 96 } 97 98 // Given a pointer to a child which can either be a Metachunk or a node, return a pointer to a 99 // node if it was a node; NULL if it was a Metachunk. 100 static Metachunk* resolve_chunk_ptr(void* p) { 101 Metachunk* c = (Metachunk*) p; 102 if (MetachunkPool::is_metachunk_pointer(c)) { 103 return c; 104 } 105 return NULL; 106 } 107 108 #ifdef ASSERT 109 // Verify a life node (one which lives in the tree). 110 static void verify_node(const qtnode_t* n); 111 static void verify_parenthood(const qtnode_t* child, const qtnode_t* parent); 112 // Given four leaf nodes, verify their chunks (must be adjacent and in order) 113 static void verify_sibling_chunks(const Metachunk* sibs[4]); 114 #endif 115 116 117 // Given a chunk c, split it into four smaller chunks. 118 // Returns pointer to the result chunk; splintered off chunks are appended to the given linked list. 119 // Returns NULL if chunk cannot be split. 120 Metachunk* split_once(Metachunk* c, Metachunk** p_splinters); 121 122 // Given a chunk, attempt to merge it with its siblings 123 // if they are free. 124 // Returns pointer to the result chunk if successful, NULL otherwise. 125 // 126 // !!! Please note that if this method returns a non-NULL value, the 127 // original chunk will be invalid and should not be accessed anymore! !!! 128 Metachunk* merge_once(Metachunk* c); 129 130 131 public: 132 133 ChunkTree() : _root(NULL) {} 134 135 // Initialize: allocate a root node and a root chunk header; return the 136 // root chunk header. 137 Metachunk* alloc_root(); 138 139 // Given a chunk c, split it recursively until you get a chunk of the given target_level. 140 // Returns pointer to the result chunk; returns split off chunks in p_splinters as linked list. 141 // Returns NULL if chunk cannot be split at least once. 142 Metachunk* split(chklvl_t target_level, Metachunk* c, Metachunk** p_splinters); 143 144 // Given a chunk, attempt to merge it recursively with its neighboring chunks. 145 // If successful (merged at least once), returns address of 146 // the merged chunk; NULL otherwise. 147 // 148 // !!! Please note that if this method returns a non-NULL value, the 149 // original chunk will be invalid and should not be accessed anymore! !!! 150 Metachunk* merge(Metachunk* c); 151 152 //// tree traversal //// 153 154 class ConstNodeClosure { 155 public: 156 // Return false to cancel traversal. 157 virtual bool do_node(const qtnode_t* node) = 0; 158 }; 159 160 class ConstChunkClosure { 161 public: 162 // Return false to cancel traversal. 163 virtual bool do_chunk(const Metachunk* c) = 0; 164 }; 165 166 167 // Iterate over all leafs in this tree. Returns true for complete traversal, 168 // false if traversal was cancelled. 169 bool iterate_nodes(ConstNodeClosure* c) const; 170 171 // Iterate over all nodes in this tree. Returns true for complete traversal, 172 // false if traversal was cancelled. 173 bool iterate_chunks(ConstChunkClosure* c) const; 174 175 176 //// Debug stuff //// 177 178 DEBUG_ONLY(void verify(bool slow);) 179 180 }; 181 182 183 /////////////////////// 184 // An C-heap allocated array of chunk trees. Used to describe fragmentation over a range of multiple root chunks. 185 class ChunkTreeArray { 186 187 const MetaWord* const _base; 188 const size_t _word_size; 189 190 ChunkTree* _arr; 191 int _num; 192 193 #ifdef ASSERT 194 void check_pointer(const MetaWord* p) const { 195 assert(p >= _base && p < _base + _word_size, "Invalid pointer"); 196 } 197 #endif 198 199 int index_by_address(const MetaWord* p) const { 200 DEBUG_ONLY(check_pointer(p);) 201 return (p - _base) / chklvl::MAX_CHUNK_WORD_SIZE; 202 } 203 204 public: 205 206 // Create an array of ChunkTree objects, all initialized to NULL, covering 207 // a given memory range. Memory range must be aligned to size of root chunks. 208 ChunkTreeArray(const MetaWord* base, size_t word_size); 209 210 ~ChunkTreeArray(); 211 212 // Given a memory address into the range the trees cover, return the corresponding 213 // tree. If none existed at this position, create it. 214 ChunkTree* get_tree_by_address(const MetaWord* p) const { 215 assert(p >= _base && p < _base + _word_size, "Invalid pointer"); 216 const int idx = index_by_address(p); 217 assert(idx < _num, "Invalid index"); 218 if (_arr[idx] == NULL) { 219 _arr[idx] = new ChunkTree(); 220 } 221 return _arr[idx]; 222 } 223 224 }; 225 226 227 } // namespace metaspace 228 229 #endif // SHARE_MEMORY_METASPACE_CHUNKTREE_HPP