1 /* 2 * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. 3 * Copyright (c) 2020 SAP SE. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. 9 * 10 * This code is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * version 2 for more details (a copy is included in the LICENSE file that 14 * accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License version 17 * 2 along with this work; if not, write to the Free Software Foundation, 18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 * 24 */ 25 26 #include "precompiled.hpp" 27 #include "memory/metaspace/chunkManager.hpp" 28 #include "memory/metaspace/counters.hpp" 29 #include "memory/metaspace/metaspaceAlignment.hpp" 30 #include "memory/metaspace/metaspaceArena.hpp" 31 #include "memory/metaspace/metaspaceArenaGrowthPolicy.hpp" 32 #include "memory/metaspace/metaspaceSettings.hpp" 33 #include "memory/metaspace/metaspaceStatistics.hpp" 34 #include "runtime/mutexLocker.hpp" 35 #include "utilities/debug.hpp" 36 #include "utilities/globalDefinitions.hpp" 37 //#define LOG_PLEASE 38 #include "metaspaceGtestCommon.hpp" 39 #include "metaspaceGtestContexts.hpp" 40 #include "metaspaceGtestSparseArray.hpp" 41 42 using metaspace::ArenaGrowthPolicy; 43 using metaspace::ChunkManager; 44 using metaspace::IntCounter; 45 using metaspace::MemRangeCounter; 46 using metaspace::MetaspaceArena; 47 using metaspace::SizeAtomicCounter; 48 using metaspace::ArenaStats; 49 using metaspace::InUseChunkStats; 50 51 // Little randomness helper 52 static bool fifty_fifty() { 53 return IntRange(100).random_value() < 50; 54 } 55 56 // A MetaspaceArenaTestBed contains a single MetaspaceArena and its lock. 57 // It keeps track of allocations done from this MetaspaceArena. 58 class MetaspaceArenaTestBed : public CHeapObj<mtInternal> { 59 60 const SizeRange _allocation_range; 61 const int _alignment_words; 62 63 MetaspaceArena* _arena; 64 Mutex* _lock; 65 size_t _size_of_last_failed_allocation; 66 67 // We keep track of all allocations done thru the MetaspaceArena to 68 // later check for overwriters. 69 struct allocation_t { 70 allocation_t* next; 71 MetaWord* p; // NULL if deallocated 72 size_t word_size; 73 void mark() { 74 mark_range(p, word_size); 75 } 76 void verify() const { 77 if (p != NULL) { 78 check_marked_range(p, word_size); 79 } 80 } 81 }; 82 83 allocation_t* _allocations; 84 85 // We count how much we did allocate and deallocate 86 MemRangeCounter _alloc_count_net; // net used bytes 87 MemRangeCounter _alloc_count_raw; // net used bytes + internal overhead 88 MemRangeCounter _dealloc_count; 89 90 // Check statistics returned by MetaspaceArena::add_to_statistics() against what 91 // we know we allocated. This is a bit flaky since MetaspaceArena has internal 92 // overhead. 93 void verify_arena_statistics() const { 94 95 ArenaStats stats; 96 _arena->add_to_statistics(&stats); 97 InUseChunkStats in_use_stats = stats.totals(); 98 99 assert(_dealloc_count.total_size() <= _alloc_count_net.total_size() && 100 _dealloc_count.count() <= _alloc_count_net.count(), "Sanity"); 101 102 // Check consistency of stats 103 ASSERT_GE(in_use_stats._word_size, in_use_stats._committed_words); 104 ASSERT_EQ(in_use_stats._committed_words, 105 in_use_stats._used_words + in_use_stats._free_words + in_use_stats._waste_words); 106 ASSERT_GE(in_use_stats._used_words, stats._free_blocks_word_size); 107 108 // Note: reasons why the outside alloc counter and the inside used counter can differ: 109 // - alignment/padding of allocations 110 // - inside used counter contains blocks in free list 111 // - free block list splinter threshold 112 // - if +MetaspaceGuardAllocations, guard costs 113 114 // Since what we deallocated may have been given back to us in a following allocation, 115 // we only know fore sure we allocated what we did not give back. 116 const size_t at_least_allocated = _alloc_count_net.total_size() - _dealloc_count.total_size(); 117 118 // At most we allocated this: 119 size_t max_word_overhead_per_alloc = align_up(4, _alignment_words); 120 // Guard fences come as a separate, secondary block 121 if (metaspace::Settings::use_allocation_guard()) { 122 max_word_overhead_per_alloc *= 2; 123 } 124 const size_t at_most_allocated = _alloc_count_raw.total_size() + max_word_overhead_per_alloc * _alloc_count_raw.count(); 125 126 ASSERT_LE(at_least_allocated, in_use_stats._used_words - stats._free_blocks_word_size); 127 ASSERT_GE(at_most_allocated, in_use_stats._used_words - stats._free_blocks_word_size); 128 } 129 130 public: 131 132 MetaspaceArena* arena() { return _arena; } 133 134 MetaspaceArenaTestBed(ChunkManager* cm, const ArenaGrowthPolicy* alloc_sequence, int alignment_words, 135 SizeAtomicCounter* used_words_counter, SizeRange allocation_range) : 136 _allocation_range(allocation_range), 137 _alignment_words(alignment_words), 138 _arena(NULL), 139 _lock(NULL), 140 _size_of_last_failed_allocation(0), 141 _allocations(NULL), 142 _alloc_count_net(), 143 _dealloc_count() 144 { 145 _lock = new Mutex(Monitor::nosafepoint, "gtest-MetaspaceArenaTestBed_lock"); 146 // Lock during space creation, since this is what happens in the VM too 147 // (see ClassLoaderData::metaspace_non_null(), which we mimick here). 148 MutexLocker ml(_lock, Mutex::_no_safepoint_check_flag); 149 _arena = new MetaspaceArena(cm, alloc_sequence, alignment_words, _lock, used_words_counter, "gtest-MetaspaceArenaTestBed-sm"); 150 } 151 152 ~MetaspaceArenaTestBed() { 153 154 verify_arena_statistics(); 155 156 allocation_t* a = _allocations; 157 while (a != NULL) { 158 allocation_t* b = a->next; 159 a->verify(); 160 FREE_C_HEAP_OBJ(a); 161 a = b; 162 } 163 164 DEBUG_ONLY(_arena->verify();) 165 166 // Delete MetaspaceArena. That should clean up all metaspace. 167 delete _arena; 168 delete _lock; 169 170 } 171 172 size_t words_allocated() const { return _alloc_count_net.total_size(); } 173 int num_allocations() const { return _alloc_count_net.count(); } 174 175 size_t size_of_last_failed_allocation() const { return _size_of_last_failed_allocation; } 176 177 size_t calc_expected_usage_for_allocated_words(size_t word_size) { 178 return metaspace::get_raw_word_size_for_requested_word_size(word_size, _alignment_words); 179 } 180 181 // Allocate a random amount. Return false if the allocation failed. 182 bool checked_random_allocate() { 183 size_t word_size = 1 + _allocation_range.random_value(); 184 MetaWord* p = _arena->allocate(word_size); 185 if (p != NULL) { 186 EXPECT_TRUE(is_aligned(p, _alignment_words * BytesPerWord)); 187 allocation_t* a = NEW_C_HEAP_OBJ(allocation_t, mtInternal); 188 a->word_size = word_size; 189 a->p = p; 190 a->mark(); 191 a->next = _allocations; 192 _allocations = a; 193 _alloc_count_net.add(word_size); 194 _alloc_count_raw.add(calc_expected_usage_for_allocated_words(word_size)); 195 if ((_alloc_count_net.count() % 20) == 0) { 196 verify_arena_statistics(); 197 DEBUG_ONLY(_arena->verify();) 198 } 199 return true; 200 } else { 201 _size_of_last_failed_allocation = word_size; 202 } 203 return false; 204 } 205 206 // Deallocate a random allocation 207 void checked_random_deallocate() { 208 allocation_t* a = _allocations; 209 while (a && a->p != NULL && os::random() % 10 != 0) { 210 a = a->next; 211 } 212 if (a != NULL && a->p != NULL) { 213 a->verify(); 214 _arena->deallocate(a->p, a->word_size); 215 _dealloc_count.add(a->word_size); 216 a->p = NULL; a->word_size = 0; 217 if ((_dealloc_count.count() % 20) == 0) { 218 verify_arena_statistics(); 219 DEBUG_ONLY(_arena->verify();) 220 } 221 } 222 } 223 224 }; // End: MetaspaceArenaTestBed 225 226 class MetaspaceArenaTest { 227 228 MetaspaceGtestContext _context; 229 230 SizeAtomicCounter _used_words_counter; 231 232 SparseArray<MetaspaceArenaTestBed*> _testbeds; 233 IntCounter _num_beds; 234 235 //////// Bed creation, destruction /////// 236 237 void create_new_test_bed_at(int slotindex, const ArenaGrowthPolicy* growth_policy, int alignment_words, SizeRange allocation_range) { 238 DEBUG_ONLY(_testbeds.check_slot_is_null(slotindex)); 239 MetaspaceArenaTestBed* bed = new MetaspaceArenaTestBed(&_context.cm(), growth_policy, alignment_words, 240 &_used_words_counter, allocation_range); 241 _testbeds.set_at(slotindex, bed); 242 _num_beds.increment(); 243 } 244 245 void create_random_test_bed_at(int slotindex) { 246 SizeRange allocation_range(1, 100); // randomize too? 247 const ArenaGrowthPolicy* growth_policy = ArenaGrowthPolicy::policy_for_space_type( 248 (fifty_fifty() ? Metaspace::StandardMetaspaceType : Metaspace::ReflectionMetaspaceType), 249 fifty_fifty()); 250 const int alignment_bytes = 251 1 << IntRange(metaspace::LogMetaspaceMinimalAlignment, 252 metaspace::LogMetaspaceMinimalAlignment + 7).random_value(); // zw 8 byte and 1K 253 create_new_test_bed_at(slotindex, growth_policy, alignment_bytes / BytesPerWord, allocation_range); 254 } 255 256 // Randomly create a random test bed at a random slot, and return its slot index 257 // (returns false if we reached max number of test beds) 258 bool create_random_test_bed() { 259 const int slot = _testbeds.random_null_slot_index(); 260 if (slot != -1) { 261 create_random_test_bed_at(slot); 262 } 263 return slot; 264 } 265 266 void delete_test_bed_at(int slotindex) { 267 DEBUG_ONLY(_testbeds.check_slot_is_not_null(slotindex)); 268 MetaspaceArenaTestBed* bed = _testbeds.at(slotindex); 269 delete bed; // This will return all its memory to the chunk manager 270 _testbeds.set_at(slotindex, NULL); 271 _num_beds.decrement(); 272 } 273 274 // Randomly delete a random test bed at a random slot 275 // Return false if there are no test beds to delete. 276 bool delete_random_test_bed() { 277 const int slotindex = _testbeds.random_non_null_slot_index(); 278 if (slotindex != -1) { 279 delete_test_bed_at(slotindex); 280 return true; 281 } 282 return false; 283 } 284 285 // Delete all test beds. 286 void delete_all_test_beds() { 287 for (int slot = _testbeds.first_non_null_slot(); slot != -1; slot = _testbeds.next_non_null_slot(slot)) { 288 delete_test_bed_at(slot); 289 } 290 } 291 292 //////// Allocating metaspace from test beds /////// 293 294 bool random_allocate_from_testbed(int slotindex) { 295 DEBUG_ONLY(_testbeds.check_slot_is_not_null(slotindex);) 296 MetaspaceArenaTestBed* bed = _testbeds.at(slotindex); 297 bool success = bed->checked_random_allocate(); 298 if (success == false) { 299 // We must have hit a limit. 300 EXPECT_LT(_context.commit_limiter().possible_expansion_words(), 301 bed->calc_expected_usage_for_allocated_words(bed->size_of_last_failed_allocation())); 302 } 303 return success; 304 } 305 306 // Allocate multiple times random sizes from a single MetaspaceArena. 307 bool random_allocate_multiple_times_from_testbed(int slotindex, int num_allocations) { 308 bool success = true; 309 int n = 0; 310 while (success && n < num_allocations) { 311 success = random_allocate_from_testbed(slotindex); 312 n++; 313 } 314 return success; 315 } 316 317 // Allocate multiple times random sizes from a single random MetaspaceArena. 318 bool random_allocate_random_times_from_random_testbed() { 319 int slot = _testbeds.random_non_null_slot_index(); 320 bool success = false; 321 if (slot != -1) { 322 const int n = IntRange(5, 20).random_value(); 323 success = random_allocate_multiple_times_from_testbed(slot, n); 324 } 325 return success; 326 } 327 328 /////// Deallocating from testbed /////////////////// 329 330 void deallocate_from_testbed(int slotindex) { 331 DEBUG_ONLY(_testbeds.check_slot_is_not_null(slotindex);) 332 MetaspaceArenaTestBed* bed = _testbeds.at(slotindex); 333 bed->checked_random_deallocate(); 334 } 335 336 void deallocate_from_random_testbed() { 337 int slot = _testbeds.random_non_null_slot_index(); 338 if (slot != -1) { 339 deallocate_from_testbed(slot); 340 } 341 } 342 343 /////// Stats /////////////////////////////////////// 344 345 int get_total_number_of_allocations() const { 346 int sum = 0; 347 for (int i = _testbeds.first_non_null_slot(); i != -1; i = _testbeds.next_non_null_slot(i)) { 348 sum += _testbeds.at(i)->num_allocations(); 349 } 350 return sum; 351 } 352 353 size_t get_total_words_allocated() const { 354 size_t sum = 0; 355 for (int i = _testbeds.first_non_null_slot(); i != -1; i = _testbeds.next_non_null_slot(i)) { 356 sum += _testbeds.at(i)->words_allocated(); 357 } 358 return sum; 359 } 360 361 public: 362 363 MetaspaceArenaTest(size_t commit_limit, int num_testbeds) 364 : _context(commit_limit), 365 _testbeds(num_testbeds), 366 _num_beds() 367 {} 368 369 ~MetaspaceArenaTest () { 370 371 delete_all_test_beds(); 372 373 } 374 375 //////////////// Tests //////////////////////// 376 377 void test() { 378 379 // In a big loop, randomly chose one of these actions 380 // - creating a test bed (simulates a new loader creation) 381 // - allocating from a test bed (simulates allocating metaspace for a loader) 382 // - (rarely) deallocate (simulates metaspace deallocation, e.g. class redefinitions) 383 // - delete a test bed (simulates collection of a loader and subsequent return of metaspace to freelists) 384 385 const int iterations = 10000; 386 387 // Lets have a ceiling on number of words allocated (this is independent from the commit limit) 388 const size_t max_allocation_size = 8 * M; 389 390 bool force_bed_deletion = false; 391 392 for (int niter = 0; niter < iterations; niter++) { 393 394 const int r = IntRange(100).random_value(); 395 396 if (force_bed_deletion || r < 10) { 397 398 force_bed_deletion = false; 399 delete_random_test_bed(); 400 401 } else if (r < 20 || _num_beds.get() < (unsigned)_testbeds.size() / 2) { 402 403 create_random_test_bed(); 404 405 } else if (r < 95) { 406 407 // If allocation fails, we hit the commit limit and should delete some beds first 408 force_bed_deletion = ! random_allocate_random_times_from_random_testbed(); 409 410 } else { 411 412 // Note: does not affect the used words counter. 413 deallocate_from_random_testbed(); 414 415 } 416 417 // If we are close to our quota, start bed deletion 418 if (_used_words_counter.get() >= max_allocation_size) { 419 420 force_bed_deletion = true; 421 422 } 423 424 } 425 426 } 427 428 }; 429 430 // 32 parallel MetaspaceArena objects, random allocating without commit limit 431 TEST_VM(metaspace, MetaspaceArena_random_allocs_32_beds_no_commit_limit) { 432 MetaspaceArenaTest test(max_uintx, 32); 433 test.test(); 434 } 435 436 // 32 parallel Metaspace arena objects, random allocating with commit limit 437 TEST_VM(metaspace, MetaspaceArena_random_allocs_32_beds_with_commit_limit) { 438 MetaspaceArenaTest test(2 * M, 32); 439 test.test(); 440 } 441 442 // A single MetaspaceArena, random allocating without commit limit. This should exercise 443 // chunk enlargement since allocation is undisturbed. 444 TEST_VM(metaspace, MetaspaceArena_random_allocs_1_bed_no_commit_limit) { 445 MetaspaceArenaTest test(max_uintx, 1); 446 test.test(); 447 } 448