1 /* 2 * Copyright (c) 2018, 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 #include "precompiled.hpp" 25 26 //#include "logging/log.hpp" 27 #include "memory/metaspace/chunkTree.hpp" 28 #include "memory/metaspace/metachunk.hpp" 29 #include "utilities/debug.hpp" 30 #include "utilities/globalDefinitions.hpp" 31 32 namespace metaspace { 33 34 // Our binary tree has 13 levels (number of chunk levels). 35 // Since the leafs of this tree are Metachunk structures, 36 // we need nodes (btnode_t) for the non-leaf nodes and 37 // Metachunk structures for the leaf nodes. Thus, a fully 38 // expanded binary tree (representing a 4MB section fragmented 39 // into 4096 1K chunks) would need 4095 nodes and 4096 metachunk structures. 40 static const u2 nodepool_max_capacity = 4095; 41 static const u2 nodepool_capacity_increase = 300; 42 43 static const u2 chunkpool_max_capacity = 4096; 44 static const u2 chunkpool_capacity_increase = 300; 45 46 ChunkTree::ChunkTree() 47 : _root((ref_t)0), 48 _nodePool("node pool", nodepool_max_capacity, nodepool_capacity_increase), 49 _chunkPool("chunk pool", chunkpool_max_capacity, chunkpool_capacity_increase) 50 {} 51 52 #ifdef ASSERT 53 54 void ChunkTree::check_is_valid_ref(ref_t ref) { 55 const u2 raw_idx = get_raw_index_from_reference(ref); 56 const u2 info = get_info_from_reference(ref); 57 switch (info) { 58 case node_marker: 59 assert(_nodePool.is_valid_index(raw_idx), "Invalid node ref: %u", (unsigned)ref); 60 break; 61 case free_chunk_marker: 62 case used_chunk_marker: 63 assert(_chunkPool.is_valid_index(raw_idx), "Invalid chunk ref: %u", (unsigned)ref); 64 break; 65 default: 66 ShouldNotReachHere(); 67 } 68 } 69 70 void ChunkTree::verify_node(const btnode_t* n) const { 71 72 const ref_t node_ref = encode_reference_for_node(n); 73 74 // Check parent. 75 const ref_t parent_ref = n->parent; 76 77 if (parent_ref == 0) { 78 assert(node_ref == _root, "This should be the root node"); 79 } else { 80 btnode_t* parent_node = resolve_reference_to_node(parent_ref); 81 assert(parent_node->child[0] == node_ref || 82 parent_node->child[1] == node_ref, "Incorrect parent backlink."); 83 } 84 85 // Check children. 86 for (int i = 0; i < 2; i ++) { 87 const ref_t child_ref = n->child[i]; 88 assert(child_ref != 0, "child ref null"); 89 90 // May be either a chunk or another node. 91 if (reference_is_chunk(child_ref)) { 92 Metachunk* c = resolve_reference_to_chunk(child_ref); 93 assert(c->tree_node_ref() == node_ref, "Incorrect parent backlink."); 94 if (reference_is_free_chunk(child_ref)) { 95 assert(c->is_free(), "free info mismatch"); 96 } else { 97 assert(c->is_in_use(), "free info mismatch"); 98 } 99 } else { 100 // This chunk is another node 101 btnode_t* n = resolve_reference_to_node(child_ref); 102 assert(n->parent == node_ref, "Incorrect parent backlink."); 103 } 104 } 105 106 } 107 #endif 108 109 110 // Initialize: allocate a root node and a root chunk header; return the 111 // root chunk header. It will be partly initialized. 112 // Note: this just allocates a memory-less header; memory itself is allocated inside VirtualSpaceNode. 113 Metachunk* ChunkTree::alloc_root_chunk_header() { 114 115 assert(_root == 0, "already have a root"); 116 117 Metachunk* c = _chunkPool.allocate_element(); 118 c->wipe(); 119 120 c->set_level(chklvl::ROOT_CHUNK_LEVEL); 121 122 const ref_t root_node_ref = encode_reference_for_chunk(c, true); 123 124 _root = root_node_ref; 125 126 return c; 127 128 } 129 130 // Given a chunk c, split it once. 131 // 132 // The original chunk must not be part of a freelist. 133 // 134 // Returns pointer to the result chunk; updates the splinters array to return the splintered off chunk. 135 // 136 // Returns NULL if chunk cannot be split any further. 137 Metachunk* ChunkTree::split_once(Metachunk* c, Metachunk* splinters[chklvl::NUM_CHUNK_LEVELS]) { 138 139 assert(c->is_free(), "Can only split free chunks."); 140 141 if (c->level() == chklvl::HIGHEST_CHUNK_LEVEL) { 142 return NULL; 143 } 144 145 const ref_t orig_chunk_ref = encode_reference_for_chunk(c, true); 146 147 // Split chunk into two chunks. 148 Metachunk* leader = c; 149 Metachunk* follower = allocate_new_chunk(); 150 follower->wipe(); 151 152 const chklvl_t new_level = c->level() + 1; 153 const size_t new_word_size = chklvl::word_size_for_level(new_level); 154 const size_t old_committed_words = c->committed_words(); 155 156 assert(new_word_size == c->word_size() / 2, "Sanity"); 157 assert(old_committed_words <= c->word_size(), "Sanity"); 158 159 leader->set_level(new_level); 160 follower->set_level(new_level); 161 162 follower->set_base(c->base() + new_word_size); 163 164 // Carry over commit boundary to new chunk; this is an optimization 165 // to avoid too frequent commit requests. 166 if (old_committed_words >= new_word_size) { 167 leader->set_committed_words(new_word_size); 168 follower->set_committed_words(old_committed_words - new_word_size); 169 } else { 170 leader->set_committed_words(old_committed_words); 171 follower->set_committed_words(0); 172 } 173 174 // Create a new parent node for the two sibling chunks. 175 btnode_t* const new_parent = allocate_new_node(); 176 const ref_t new_parent_ref = encode_reference_for_node(new_parent); 177 178 // Replace the reference to the original chunk in the parent node with 179 // the reference to the parent node. 180 if (_root == orig_chunk_ref) { 181 assert(new_level == chklvl::ROOT_CHUNK_LEVEL + 1, "Original chunk should have been root chunk."); 182 assert(c->tree_node_ref() == 0, "Original chunk should not have a parent."); 183 _root = new_parent_ref; 184 } else { 185 const ref_t old_parent_ref = c->tree_node_ref(); 186 btnode_t* const old_parent = resolve_reference_to_node(old_parent_ref); 187 assert(old_parent != NULL, "Original chunk should have a parent."); 188 if (old_parent->child[0] == orig_chunk_ref) { 189 old_parent->child[0] = new_parent_ref; 190 } else if (old_parent->child[1] == orig_chunk_ref) { 191 old_parent->child[1] = new_parent_ref; 192 } else { 193 ShouldNotReachHere(); 194 } 195 } 196 197 // Tell the two new chunks who their parent is. 198 c->set_tree_node_ref(new_parent_ref); 199 follower->set_tree_node_ref(new_parent_ref); 200 201 // Remember the splintered off chunk in the splinters array 202 assert(splinters[new_level] == NULL, "Sanity"); 203 splinters[new_level] = follower; 204 205 return c; 206 207 } 208 209 // Given a chunk, attempt to merge it with its sibling if it is free. 210 // Returns pointer to the result chunk if successful, NULL otherwise. 211 // 212 // Returns number of merged chunks, by chunk level, in num_merged array. These numbers 213 // includes the original chunk. 214 // 215 // !!! Please note that if this method returns a non-NULL value, the 216 // original chunk will be invalid and should not be accessed anymore! !!! 217 Metachunk* ChunkTree::merge_once(Metachunk* c, int num_merged[chklvl::NUM_CHUNK_LEVELS]) { 218 219 assert(c->is_free(), "Only call for free chunks."); 220 221 const chklvl_t orig_level = c->level(); 222 223 // If chunk is already a root chunk, we cannot merge any further. 224 if (orig_level == chklvl::ROOT_CHUNK_LEVEL) { 225 return NULL; 226 } 227 228 const ref_t orig_chunk_ref = encode_reference_for_chunk(c, true); 229 230 // Get parent node and parent ref. 231 const ref_t parent_ref = c->tree_node_ref(); 232 btnode_t* const parent = resolve_reference_to_node(parent_ref); 233 assert(parent != NULL, "Chunk should have a parent."); // Since we left for root chunks already 234 235 // Check if sibling are free and not splintered. 236 if (reference_is_free_chunk(parent->child[0]) == false || 237 reference_is_free_chunk(parent->child[1]) == false) { 238 return NULL; 239 } 240 241 // Merge chunks. 242 Metachunk* c1 = resolve_reference_to_chunk(parent->child[0]); 243 Metachunk* c2 = resolve_reference_to_chunk(parent->child[1]); 244 245 // At this point, both metachunks should be free and not splintered. 246 assert(c1->is_free() && c2->is_free() && c1->level() == c2->level(), "Sanity"); 247 248 // Find out who is leader. Let the leader live on. 249 Metachunk* leader = c1; 250 Metachunk* follower = c2; 251 252 if (c1->base() > c2->base()) { 253 leader = c2; follower = c1; 254 } 255 256 // Chunk memory should be adjacent to each other. 257 assert(leader->base() + leader->word_size() == follower->base(), "Wrong geometry"); 258 259 leader->set_level(orig_level - 1); 260 261 // Carry over committed words. 262 size_t new_committed_words = leader->committed_words(); 263 if (leader->is_fully_committed()) { 264 new_committed_words += follower->committed_words(); 265 } 266 assert(new_committed_words <= leader->word_size(), "Sanity"); 267 leader->set_committed_words(new_committed_words); 268 269 const ref_t leader_ref = encode_reference_for_chunk(leader, true); 270 271 // Re-hang new merged chunk in tree one level up. 272 const ref_t grand_parent_ref = parent->parent; 273 if (grand_parent_ref == 0) { 274 // Seems old parent node was root. That means we should have a root chunk now. 275 assert(leader->level() == chklvl::ROOT_CHUNK_LEVEL, "Sanity"); 276 // Which we just hang under _root... 277 _root = leader_ref; 278 } else { 279 btnode_t* grand_parent = resolve_reference_to_node(grand_parent_ref); 280 if (grand_parent->child[0] == parent_ref) { 281 grand_parent->child[0] = leader_ref; 282 } else if (grand_parent->child[1] == parent_ref) { 283 grand_parent->child[1] = leader_ref; 284 } else { 285 ShouldNotReachHere(); 286 } 287 } 288 289 // Remove the follower from its free list 290 follower->remove_from_list(); 291 292 // Adjust stats 293 num_merged[orig_level] ++; 294 295 // Release the superfluous chunk header, and the old parent node. 296 release_chunk(follower); 297 release_node(parent); 298 299 return leader; 300 301 } 302 303 // Given a chunk c, split it recursively until you get a chunk of the given target_level. 304 // 305 // The original chunk must not be part of a freelist. 306 // 307 // Returns pointer to the result chunk; returns split off chunks in splinters array. 308 // 309 // Returns NULL if chunk cannot be split at least once. 310 Metachunk* ChunkTree::split(chklvl_t target_level, Metachunk* c, Metachunk* splinters[chklvl::NUM_CHUNK_LEVELS]) { 311 312 DEBUG_ONLY(chklvl::check_valid_level(target_level)); 313 assert(target_level > c->level(), "Wrong target level"); 314 315 Metachunk* c_last = split_once(c, splinters); 316 while (c_last != NULL && c_last->level() < target_level) { 317 c_last = split_once(c, splinters); 318 } 319 320 assert(c != NULL, "Sanity"); 321 322 return c; 323 324 } 325 326 // Given a chunk, attempt to merge it recursively with its neighboring chunks. 327 // 328 // If successful (merged at least once), returns address of 329 // the merged chunk; NULL otherwise. 330 // 331 // The merged chunks are removed from their freelist; the number of merged chunks is 332 // returned, split by level, in num_merged array. Note that these numbers does not 333 // include the original chunk. 334 // 335 // !!! Please note that if this method returns a non-NULL value, the 336 // original chunk will be invalid and should not be accessed anymore! !!! 337 Metachunk* ChunkTree::merge(Metachunk* c, int num_merged[chklvl::NUM_CHUNK_LEVELS]) { 338 339 DEBUG_ONLY(c->verify(false);) 340 assert(c->is_free(), "Only merge free chunks."); 341 342 assert(c->level() > chklvl::ROOT_CHUNK_LEVEL, "Do not merge root chunks"); 343 344 // Original chunk should be outside the chunk manager freelist before attempting to merge. 345 assert(c->prev() == NULL && c->next() == NULL, "Remove chunk from freelist before merging."); 346 347 Metachunk* c_last = merge_once(c, num_merged); 348 while (c_last != NULL && c_last->level() > chklvl::ROOT_CHUNK_LEVEL) { 349 c_last = merge_once(c, num_merged); 350 } 351 352 return c_last; 353 } 354 355 356 //// tree traversal //// 357 358 bool ChunkTree::iterate_chunks_helper(ref_t ref, ChunkClosure* cc) const { 359 if (reference_is_chunk(ref)) { 360 Metachunk* const c = resolve_reference_to_chunk(ref); 361 return cc->do_chunk(c); 362 } else { 363 btnode_t* const n = resolve_reference_to_node(ref); 364 assert(n->child[0] != 0 && n->child[1] != 0, "nodes have always children"); 365 return iterate_chunks_helper(n->child[0], cc) && 366 iterate_chunks_helper(n->child[1], cc); 367 } 368 369 return true; 370 } 371 372 // Iterate over all nodes in this tree. Returns true for complete traversal, 373 // false if traversal was cancelled. 374 bool ChunkTree::iterate_chunks(ChunkClosure* cc) const { 375 376 if (_root == 0) { 377 return true; 378 } 379 380 return iterate_chunks_helper(_root, cc); 381 } 382 383 384 385 #ifdef ASSERT 386 387 // Helper for verify() 388 void ChunkTree::verify_helper(bool slow, ref_t ref, const MetaWord* p, int* num_chunks, int* num_nodes) const { 389 390 if (reference_is_node(ref)) { 391 392 // A node 393 394 const btnode_t* const n = resolve_reference_to_node(ref); 395 396 verify_node(n); 397 398 (*num_nodes) ++; 399 400 // Verify childs recursively. 401 verify_helper(slow, n->child[0], p, num_chunks, num_nodes); 402 verify_helper(slow, n->child[1], p, num_chunks, num_nodes); 403 404 } else { 405 406 // A chunk. 407 408 const Metachunk* const c = resolve_reference_to_chunk(ref); 409 410 assert(c->is_free() == reference_is_free_chunk(ref), "free info mismatch"); 411 412 // Chunks in the tree should be ordered by base address of the range 413 // they represent, and there should be no address holes. 414 if (p == NULL) { 415 p = c->base() + c->word_size(); 416 } else { 417 assert(c->base() == p, "Chunk base address mismatch."); 418 } 419 420 c->verify(slow); 421 422 (*num_chunks) ++; 423 424 } 425 426 } 427 428 void ChunkTree::verify(bool slow, const MetaWord* base) const { 429 430 int num_chunks = 0; 431 int num_nodes = 0; 432 433 verify_helper(slow, _root, base, &num_chunks, &num_nodes); 434 435 // Number of chunks in the tree should be equal to the number of chunks 436 // taken from the pool; same for nodes. 437 assert(num_chunks == _chunkPool.used(), "chunk count mismatch"); 438 assert(num_nodes == _nodePool.used(), "node count mismatch"); 439 440 } 441 442 #endif // ASSERT 443 444 // Returns the footprint of this tree, in words. 445 size_t ChunkTree::memory_footprint_words() const { 446 return _nodePool.memory_footprint_words() + _chunkPool.memory_footprint_words() + (sizeof(this) / BytesPerWord); 447 } 448 449 450 451 452 //// ChunkTreeArray /////////////777 453 454 // Create an array of ChunkTree objects, all initialized to NULL, covering 455 // a given memory range. Memory range must be aligned to size of root chunks. 456 ChunkTreeArray::ChunkTreeArray(const MetaWord* base, size_t word_size) 457 : _base(base), _word_size(word_size), 458 _arr(NULL), _num(0) 459 { 460 assert(is_aligned(_word_size, chklvl::MAX_CHUNK_WORD_SIZE), "not aligned"); 461 _num = _word_size / chklvl::MAX_CHUNK_WORD_SIZE; 462 _arr = NEW_C_HEAP_ARRAY(ChunkTree*, _num, mtInternal); 463 for (int i = 0; i < _num; i ++) { 464 _arr[i] = NULL; 465 } 466 } 467 468 ChunkTreeArray::~ChunkTreeArray() { 469 FREE_C_HEAP_ARRAY(ChunkTree*, _arr); 470 } 471 472 473 // Iterate over all nodes in all trees. Returns true for complete traversal, 474 // false if traversal was cancelled. 475 bool ChunkTreeArray::iterate_chunks(ChunkClosure* cc) const { 476 for (int i = 0; i < _num; i ++) { 477 if (_arr[i] != NULL) { 478 if (_arr[i]->iterate_chunks(cc) == false) { 479 return false; 480 } 481 } 482 } 483 return true; 484 } 485 486 #ifdef ASSERT 487 void ChunkTreeArray::verify(bool slow) const { 488 for (int i = 0; i < _num; i ++) { 489 if (_arr[i] != NULL) { 490 _arr[i]->verify(slow, _base); 491 } 492 } 493 } 494 #endif 495 496 // Returns the footprint of all trees in this array, in words. 497 size_t ChunkTreeArray::memory_footprint_words() const { 498 size_t s = 0; 499 for (int i = 0; i < _num; i ++) { 500 if (_arr[i] != NULL) { 501 s += _arr[i]->memory_footprint_words(); 502 } 503 } 504 return s; 505 } 506 507 } // namespace metaspace 508 509