1 /* 2 * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. 3 * Copyright (c) 2020 SAP SE. All rights reserved. 4 * Copyright (c) 2023 Red Hat, Inc. All rights reserved. 5 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6 * 7 * This code is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 only, as 9 * published by the Free Software Foundation. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 * 25 */ 26 27 #ifndef SHARE_MEMORY_METASPACE_BINLIST_HPP 28 #define SHARE_MEMORY_METASPACE_BINLIST_HPP 29 30 #include "memory/metaspace/counters.hpp" 31 #include "memory/metaspace/metablock.hpp" 32 #include "memory/metaspace/metaspaceCommon.hpp" 33 #include "utilities/align.hpp" 34 #include "utilities/debug.hpp" 35 #include "utilities/globalDefinitions.hpp" 36 37 namespace metaspace { 38 39 // BinList is a data structure to manage small to very small memory blocks 40 // (only a few words). It is used to manage deallocated small blocks. 41 42 // Memory blocks are kept in a vector of linked lists of equi-sized blocks: 43 // 44 // wordsize 45 // 46 // +---+ +---+ +---+ +---+ 47 // 1 | |-->| |-->| |-...->| | 48 // +---+ +---+ +---+ +---+ 49 // 50 // +----+ +----+ +----+ +----+ 51 // 2 | |-->| |-->| |-...->| | 52 // +----+ +----+ +----+ +----+ 53 // 54 // +-----+ +-----+ +-----+ +-----+ 55 // 3 | |-->| |-->| |-...->| | 56 // +-----+ +-----+ +-----+ +-----+ 57 // . 58 // . 59 // . 60 // 61 // +----------+ +----------+ +----------+ +----------+ 62 // n | |-->| |-->| |-...->| | 63 // +----------+ +----------+ +----------+ +----------+ 64 65 // Insertion is of course fast, O(1). 66 // 67 // On retrieval, we attempt to find the closest fit to a given size, walking the 68 // list head vector (a bitmask is used to speed that part up). 69 // 70 // This structure is a bit expensive in memory costs (we pay one pointer per managed 71 // block size) so we only use it for a small number of sizes. 72 73 template <int num_lists> 74 class BinListImpl { 75 76 struct Block { 77 Block* const _next; 78 Block(Block* next) : _next(next) {} 79 }; 80 81 #define BLOCK_FORMAT "Block @" PTR_FORMAT ": size: " SIZE_FORMAT ", next: " PTR_FORMAT 82 #define BLOCK_FORMAT_ARGS(b, sz) p2i(b), (sz), p2i((b)->_next) 83 84 // Block size must be exactly one word size. 85 STATIC_ASSERT(sizeof(Block) == BytesPerWord); 86 STATIC_ASSERT(num_lists > 0); 87 88 public: 89 90 // Minimal word size a block must have to be manageable by this structure. 91 const static size_t MinWordSize = 1; 92 93 // Maximal (incl) word size a block can have to be manageable by this structure. 94 const static size_t MaxWordSize = num_lists; 95 96 private: 97 98 Block* _blocks[num_lists]; 99 100 MemRangeCounter _counter; 101 102 // Given a word size, returns the index of the list holding blocks of that size 103 static int index_for_word_size(size_t word_size) { 104 int index = (int)(word_size - MinWordSize); 105 assert(index >= 0 && index < num_lists, "Invalid index %d", index); 106 return index; 107 } 108 109 // Given an index of a list, return the word size that list serves 110 static size_t word_size_for_index(int index) { 111 assert(index >= 0 && index < num_lists, "Invalid index %d", index); 112 return index + MinWordSize; 113 } 114 115 // Search the range [index, _num_lists) for the smallest non-empty list. Returns -1 on fail. 116 int index_for_next_non_empty_list(int index) { 117 assert(index >= 0 && index < num_lists, "Invalid index %d", index); 118 int i2 = index; 119 while (i2 < num_lists && _blocks[i2] == nullptr) { 120 i2 ++; 121 } 122 return i2 == num_lists ? -1 : i2; 123 } 124 125 #ifdef ASSERT 126 static const uintptr_t canary = 0xFFEEFFEE; 127 static void write_canary(MetaWord* p, size_t word_size) { 128 if (word_size > 1) { // 1-word-sized blocks have no space for a canary 129 ((uintptr_t*)p)[word_size - 1] = canary; 130 } 131 } 132 static bool check_canary(const Block* b, size_t word_size) { 133 return word_size == 1 || // 1-word-sized blocks have no space for a canary 134 ((const uintptr_t*)b)[word_size - 1] == canary; 135 } 136 #endif 137 138 public: 139 140 BinListImpl() { 141 for (int i = 0; i < num_lists; i++) { 142 _blocks[i] = nullptr; 143 } 144 } 145 146 void add_block(MetaBlock mb) { 147 assert(!mb.is_empty(), "Don't add empty blocks"); 148 const size_t word_size = mb.word_size(); 149 MetaWord* const p = mb.base(); 150 assert(word_size >= MinWordSize && 151 word_size <= MaxWordSize, "bad block size"); 152 DEBUG_ONLY(write_canary(p, word_size);) 153 const int index = index_for_word_size(word_size); 154 Block* old_head = _blocks[index]; 155 Block* new_head = new (p) Block(old_head); 156 _blocks[index] = new_head; 157 _counter.add(word_size); 158 } 159 160 // Given a word_size, searches and returns a block of at least that size. 161 // Block may be larger. 162 MetaBlock remove_block(size_t word_size) { 163 assert(word_size >= MinWordSize && 164 word_size <= MaxWordSize, "bad block size " SIZE_FORMAT ".", word_size); 165 MetaBlock result; 166 int index = index_for_word_size(word_size); 167 index = index_for_next_non_empty_list(index); 168 if (index != -1) { 169 Block* b = _blocks[index]; 170 const size_t real_word_size = word_size_for_index(index); 171 assert(b != nullptr, "Sanity"); 172 assert(check_canary(b, real_word_size), 173 "bad block in list[%d] (" BLOCK_FORMAT ")", index, BLOCK_FORMAT_ARGS(b, real_word_size)); 174 _blocks[index] = b->_next; 175 _counter.sub(real_word_size); 176 result = MetaBlock((MetaWord*)b, real_word_size); 177 } 178 return result; 179 } 180 181 // Returns number of blocks in this structure 182 unsigned count() const { return _counter.count(); } 183 184 // Returns total size, in words, of all elements. 185 size_t total_size() const { return _counter.total_size(); } 186 187 bool is_empty() const { return count() == 0; } 188 189 #ifdef ASSERT 190 void verify() const { 191 MemRangeCounter local_counter; 192 for (int i = 0; i < num_lists; i++) { 193 const size_t s = word_size_for_index(i); 194 int pos = 0; 195 Block* b_last = nullptr; // catch simple circularities 196 for (Block* b = _blocks[i]; b != nullptr; b = b->_next, pos++) { 197 assert(check_canary(b, s), ""); 198 assert(b != b_last, "Circle"); 199 local_counter.add(s); 200 b_last = b; 201 } 202 if (UseNewCode)printf("\n"); 203 } 204 local_counter.check(_counter); 205 } 206 #endif // ASSERT 207 208 }; 209 210 typedef BinListImpl<32> BinList32; 211 212 } // namespace metaspace 213 214 #endif // SHARE_MEMORY_METASPACE_BINLIST_HPP