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/metaspaceArena.hpp" 30 #include "memory/metaspace/metaspaceArenaGrowthPolicy.hpp" 31 #include "memory/metaspace/metaspaceSettings.hpp" 32 #include "memory/metaspace/metaspaceStatistics.hpp" 33 #include "runtime/mutexLocker.hpp" 34 #include "utilities/debug.hpp" 35 #include "utilities/globalDefinitions.hpp" 36 //#define LOG_PLEASE 37 #include "metaspaceGtestCommon.hpp" 38 #include "metaspaceGtestContexts.hpp" 39 #include "metaspaceGtestSparseArray.hpp" 40 41 using metaspace::ArenaGrowthPolicy; 42 using metaspace::ChunkManager; 43 using metaspace::IntCounter; 44 using metaspace::MemRangeCounter; 45 using metaspace::MetaspaceArena; 46 using metaspace::SizeAtomicCounter; 47 using metaspace::ArenaStats; 48 using metaspace::InUseChunkStats; 49 50 // Little randomness helper 51 static bool fifty_fifty() { 52 return IntRange(100).random_value() < 50; 53 } 54 55 // See metaspaceArena.cpp : needed for predicting commit sizes. 56 namespace metaspace { 57 extern size_t get_raw_word_size_for_requested_word_size(size_t net_word_size); 58 } 59 60 // A MetaspaceArenaTestBed contains a single MetaspaceArena and its lock. 61 // It keeps track of allocations done from this MetaspaceArena. 62 class MetaspaceArenaTestBed : public CHeapObj<mtInternal> { 63 64 MetaspaceArena* _arena; 65 66 Mutex* _lock; 67 68 const SizeRange _allocation_range; 69 size_t _size_of_last_failed_allocation; 70 71 // We keep track of all allocations done thru the MetaspaceArena to 72 // later check for overwriters. 73 struct allocation_t { 74 allocation_t* next; 75 MetaWord* p; // NULL if deallocated 76 size_t word_size; 77 void mark() { 78 mark_range(p, word_size); 79 } 80 void verify() const { 81 if (p != NULL) { 82 check_marked_range(p, word_size); 83 } 84 } 85 }; 86 87 allocation_t* _allocations; 88 89 // We count how much we did allocate and deallocate 90 MemRangeCounter _alloc_count; 91 MemRangeCounter _dealloc_count; 92 93 // Check statistics returned by MetaspaceArena::add_to_statistics() against what 94 // we know we allocated. This is a bit flaky since MetaspaceArena has internal 95 // overhead. 96 void verify_arena_statistics() const { 97 98 ArenaStats stats; 99 _arena->add_to_statistics(&stats); 100 InUseChunkStats in_use_stats = stats.totals(); 101 102 assert(_dealloc_count.total_size() <= _alloc_count.total_size() && 103 _dealloc_count.count() <= _alloc_count.count(), "Sanity"); 104 105 // Check consistency of stats 106 ASSERT_GE(in_use_stats._word_size, in_use_stats._committed_words); 107 ASSERT_EQ(in_use_stats._committed_words, 108 in_use_stats._used_words + in_use_stats._free_words + in_use_stats._waste_words); 109 ASSERT_GE(in_use_stats._used_words, stats._free_blocks_word_size); 110 111 // Note: reasons why the outside alloc counter and the inside used counter can differ: 112 // - alignment/padding of allocations 113 // - inside used counter contains blocks in free list 114 // - free block list splinter threshold 115 // - if +MetaspaceGuardAllocations, guard costs 116 117 // Since what we deallocated may have been given back to us in a following allocation, 118 // we only know fore sure we allocated what we did not give back. 119 const size_t at_least_allocated = _alloc_count.total_size() - _dealloc_count.total_size(); 120 121 // At most we allocated this: 122 const size_t max_word_overhead_per_alloc = 123 4 + (metaspace::Settings::use_allocation_guard() ? 4 : 0); 124 const size_t at_most_allocated = _alloc_count.total_size() + max_word_overhead_per_alloc * _alloc_count.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 131 public: 132 133 MetaspaceArena* arena() { return _arena; } 134 135 MetaspaceArenaTestBed(ChunkManager* cm, const ArenaGrowthPolicy* alloc_sequence, 136 SizeAtomicCounter* used_words_counter, SizeRange allocation_range) : 137 _arena(NULL), 138 _lock(NULL), 139 _allocation_range(allocation_range), 140 _size_of_last_failed_allocation(0), 141 _allocations(NULL), 142 _alloc_count(), 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, _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.total_size(); } 173 int num_allocations() const { return _alloc_count.count(); } 174 175 size_t size_of_last_failed_allocation() const { return _size_of_last_failed_allocation; } 176 177 // Allocate a random amount. Return false if the allocation failed. 178 bool checked_random_allocate() { 179 size_t word_size = 1 + _allocation_range.random_value(); 180 MetaWord* p = _arena->allocate(word_size); 181 if (p != NULL) { 182 EXPECT_TRUE(is_aligned(p, sizeof(MetaWord))); 183 allocation_t* a = NEW_C_HEAP_OBJ(allocation_t, mtInternal); 184 a->word_size = word_size; 185 a->p = p; 186 a->mark(); 187 a->next = _allocations; 188 _allocations = a; 189 _alloc_count.add(word_size); 190 if ((_alloc_count.count() % 20) == 0) { 191 verify_arena_statistics(); 192 DEBUG_ONLY(_arena->verify();) 193 } 194 return true; 195 } else { 196 _size_of_last_failed_allocation = word_size; 197 } 198 return false; 199 } 200 201 // Deallocate a random allocation 202 void checked_random_deallocate() { 203 allocation_t* a = _allocations; 204 while (a && a->p != NULL && os::random() % 10 != 0) { 205 a = a->next; 206 } 207 if (a != NULL && a->p != NULL) { 208 a->verify(); 209 _arena->deallocate(a->p, a->word_size); 210 _dealloc_count.add(a->word_size); 211 a->p = NULL; a->word_size = 0; 212 if ((_dealloc_count.count() % 20) == 0) { 213 verify_arena_statistics(); 214 DEBUG_ONLY(_arena->verify();) 215 } 216 } 217 } 218 219 }; // End: MetaspaceArenaTestBed 220 221 class MetaspaceArenaTest { 222 223 MetaspaceGtestContext _context; 224 225 SizeAtomicCounter _used_words_counter; 226 227 SparseArray<MetaspaceArenaTestBed*> _testbeds; 228 IntCounter _num_beds; 229 230 //////// Bed creation, destruction /////// 231 232 void create_new_test_bed_at(int slotindex, const ArenaGrowthPolicy* growth_policy, SizeRange allocation_range) { 233 DEBUG_ONLY(_testbeds.check_slot_is_null(slotindex)); 234 MetaspaceArenaTestBed* bed = new MetaspaceArenaTestBed(&_context.cm(), growth_policy, 235 &_used_words_counter, allocation_range); 236 _testbeds.set_at(slotindex, bed); 237 _num_beds.increment(); 238 } 239 240 void create_random_test_bed_at(int slotindex) { 241 SizeRange allocation_range(1, 100); // randomize too? 242 const ArenaGrowthPolicy* growth_policy = ArenaGrowthPolicy::policy_for_space_type( 243 (fifty_fifty() ? Metaspace::StandardMetaspaceType : Metaspace::ReflectionMetaspaceType), 244 fifty_fifty()); 245 create_new_test_bed_at(slotindex, growth_policy, allocation_range); 246 } 247 248 // Randomly create a random test bed at a random slot, and return its slot index 249 // (returns false if we reached max number of test beds) 250 bool create_random_test_bed() { 251 const int slot = _testbeds.random_null_slot_index(); 252 if (slot != -1) { 253 create_random_test_bed_at(slot); 254 } 255 return slot; 256 } 257 258 // Create test beds for all slots 259 void create_all_test_beds() { 260 for (int slot = 0; slot < _testbeds.size(); slot++) { 261 if (_testbeds.slot_is_null(slot)) { 262 create_random_test_bed_at(slot); 263 } 264 } 265 } 266 267 void delete_test_bed_at(int slotindex) { 268 DEBUG_ONLY(_testbeds.check_slot_is_not_null(slotindex)); 269 MetaspaceArenaTestBed* bed = _testbeds.at(slotindex); 270 delete bed; // This will return all its memory to the chunk manager 271 _testbeds.set_at(slotindex, NULL); 272 _num_beds.decrement(); 273 } 274 275 // Randomly delete a random test bed at a random slot 276 // Return false if there are no test beds to delete. 277 bool delete_random_test_bed() { 278 const int slotindex = _testbeds.random_non_null_slot_index(); 279 if (slotindex != -1) { 280 delete_test_bed_at(slotindex); 281 return true; 282 } 283 return false; 284 } 285 286 // Delete all test beds. 287 void delete_all_test_beds() { 288 for (int slot = _testbeds.first_non_null_slot(); slot != -1; slot = _testbeds.next_non_null_slot(slot)) { 289 delete_test_bed_at(slot); 290 } 291 } 292 293 //////// Allocating metaspace from test beds /////// 294 295 bool random_allocate_from_testbed(int slotindex) { 296 DEBUG_ONLY(_testbeds.check_slot_is_not_null(slotindex);) 297 MetaspaceArenaTestBed* bed = _testbeds.at(slotindex); 298 bool success = bed->checked_random_allocate(); 299 if (success == false) { 300 // We must have hit a limit. 301 EXPECT_LT(_context.commit_limiter().possible_expansion_words(), 302 metaspace::get_raw_word_size_for_requested_word_size(bed->size_of_last_failed_allocation())); 303 } 304 return success; 305 } 306 307 // Allocate multiple times random sizes from a single MetaspaceArena. 308 bool random_allocate_multiple_times_from_testbed(int slotindex, int num_allocations) { 309 bool success = true; 310 int n = 0; 311 while (success && n < num_allocations) { 312 success = random_allocate_from_testbed(slotindex); 313 n++; 314 } 315 return success; 316 } 317 318 // Allocate multiple times random sizes from a single random MetaspaceArena. 319 bool random_allocate_random_times_from_random_testbed() { 320 int slot = _testbeds.random_non_null_slot_index(); 321 bool success = false; 322 if (slot != -1) { 323 const int n = IntRange(5, 20).random_value(); 324 success = random_allocate_multiple_times_from_testbed(slot, n); 325 } 326 return success; 327 } 328 329 /////// Deallocating from testbed /////////////////// 330 331 void deallocate_from_testbed(int slotindex) { 332 DEBUG_ONLY(_testbeds.check_slot_is_not_null(slotindex);) 333 MetaspaceArenaTestBed* bed = _testbeds.at(slotindex); 334 bed->checked_random_deallocate(); 335 } 336 337 void deallocate_from_random_testbed() { 338 int slot = _testbeds.random_non_null_slot_index(); 339 if (slot != -1) { 340 deallocate_from_testbed(slot); 341 } 342 } 343 344 /////// Stats /////////////////////////////////////// 345 346 int get_total_number_of_allocations() const { 347 int sum = 0; 348 for (int i = _testbeds.first_non_null_slot(); i != -1; i = _testbeds.next_non_null_slot(i)) { 349 sum += _testbeds.at(i)->num_allocations(); 350 } 351 return sum; 352 } 353 354 size_t get_total_words_allocated() const { 355 size_t sum = 0; 356 for (int i = _testbeds.first_non_null_slot(); i != -1; i = _testbeds.next_non_null_slot(i)) { 357 sum += _testbeds.at(i)->words_allocated(); 358 } 359 return sum; 360 } 361 362 public: 363 364 MetaspaceArenaTest(size_t commit_limit, int num_testbeds) 365 : _context(commit_limit), 366 _testbeds(num_testbeds), 367 _num_beds() 368 {} 369 370 ~MetaspaceArenaTest () { 371 372 delete_all_test_beds(); 373 374 } 375 376 //////////////// Tests //////////////////////// 377 378 void test() { 379 380 // In a big loop, randomly chose one of these actions 381 // - creating a test bed (simulates a new loader creation) 382 // - allocating from a test bed (simulates allocating metaspace for a loader) 383 // - (rarely) deallocate (simulates metaspace deallocation, e.g. class redefinitions) 384 // - delete a test bed (simulates collection of a loader and subsequent return of metaspace to freelists) 385 386 const int iterations = 10000; 387 388 // Lets have a ceiling on number of words allocated (this is independent from the commit limit) 389 const size_t max_allocation_size = 8 * M; 390 391 bool force_bed_deletion = false; 392 393 for (int niter = 0; niter < iterations; niter++) { 394 395 const int r = IntRange(100).random_value(); 396 397 if (force_bed_deletion || r < 10) { 398 399 force_bed_deletion = false; 400 delete_random_test_bed(); 401 402 } else if (r < 20 || _num_beds.get() < (unsigned)_testbeds.size() / 2) { 403 404 create_random_test_bed(); 405 406 } else if (r < 95) { 407 408 // If allocation fails, we hit the commit limit and should delete some beds first 409 force_bed_deletion = ! random_allocate_random_times_from_random_testbed(); 410 411 } else { 412 413 // Note: does not affect the used words counter. 414 deallocate_from_random_testbed(); 415 416 } 417 418 // If we are close to our quota, start bed deletion 419 if (_used_words_counter.get() >= max_allocation_size) { 420 421 force_bed_deletion = true; 422 423 } 424 425 } 426 427 } 428 429 }; 430 431 // 32 parallel MetaspaceArena objects, random allocating without commit limit 432 TEST_VM(metaspace, MetaspaceArena_random_allocs_32_beds_no_commit_limit) { 433 MetaspaceArenaTest test(max_uintx, 32); 434 test.test(); 435 } 436 437 // 32 parallel Metaspace arena objects, random allocating with commit limit 438 TEST_VM(metaspace, MetaspaceArena_random_allocs_32_beds_with_commit_limit) { 439 MetaspaceArenaTest test(2 * M, 32); 440 test.test(); 441 } 442 443 // A single MetaspaceArena, random allocating without commit limit. This should exercise 444 // chunk enlargement since allocation is undisturbed. 445 TEST_VM(metaspace, MetaspaceArena_random_allocs_1_bed_no_commit_limit) { 446 MetaspaceArenaTest test(max_uintx, 1); 447 test.test(); 448 } 449