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/commitLimiter.hpp"
28 #include "memory/metaspace/counters.hpp"
29 #include "memory/metaspace/internalStats.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/mutex.hpp"
35 #include "runtime/mutexLocker.hpp"
36 #include "utilities/debug.hpp"
37 #include "utilities/globalDefinitions.hpp"
38
39 //#define LOG_PLEASE
40 #include "metaspaceGtestCommon.hpp"
41 #include "metaspaceGtestContexts.hpp"
42 #include "metaspaceGtestRangeHelpers.hpp"
43
44 using metaspace::ArenaGrowthPolicy;
45 using metaspace::CommitLimiter;
46 using metaspace::InternalStats;
47 using metaspace::MemRangeCounter;
48 using metaspace::MetaspaceArena;
49 using metaspace::SizeAtomicCounter;
50 using metaspace::Settings;
51 using metaspace::ArenaStats;
52
53 // See metaspaceArena.cpp : needed for predicting commit sizes.
54 namespace metaspace {
55 extern size_t get_raw_word_size_for_requested_word_size(size_t net_word_size);
56 }
57
58 class MetaspaceArenaTestHelper {
59
60 MetaspaceGtestContext& _context;
61
62 Mutex* _lock;
63 const ArenaGrowthPolicy* _growth_policy;
64 SizeAtomicCounter _used_words_counter;
65 MetaspaceArena* _arena;
66
initialize(const ArenaGrowthPolicy * growth_policy,const char * name="gtest-MetaspaceArena")67 void initialize(const ArenaGrowthPolicy* growth_policy, const char* name = "gtest-MetaspaceArena") {
68 _growth_policy = growth_policy;
69 _lock = new Mutex(Monitor::native, "gtest-MetaspaceArenaTest-lock", false, Monitor::_safepoint_check_never);
70 // Lock during space creation, since this is what happens in the VM too
71 // (see ClassLoaderData::metaspace_non_null(), which we mimick here).
72 {
73 MutexLocker ml(_lock, Mutex::_no_safepoint_check_flag);
74 _arena = new MetaspaceArena(&_context.cm(), _growth_policy, _lock, &_used_words_counter, name);
75 }
76 DEBUG_ONLY(_arena->verify());
77
78 }
79
80 public:
81
82 // Create a helper; growth policy for arena is determined by the given spacetype|class tupel
MetaspaceArenaTestHelper(MetaspaceGtestContext & helper,Metaspace::MetaspaceType space_type,bool is_class,const char * name="gtest-MetaspaceArena")83 MetaspaceArenaTestHelper(MetaspaceGtestContext& helper,
84 Metaspace::MetaspaceType space_type, bool is_class,
85 const char* name = "gtest-MetaspaceArena") :
86 _context(helper)
87 {
88 initialize(ArenaGrowthPolicy::policy_for_space_type(space_type, is_class), name);
89 }
90
91 // Create a helper; growth policy is directly specified
MetaspaceArenaTestHelper(MetaspaceGtestContext & helper,const ArenaGrowthPolicy * growth_policy,const char * name="gtest-MetaspaceArena")92 MetaspaceArenaTestHelper(MetaspaceGtestContext& helper, const ArenaGrowthPolicy* growth_policy,
93 const char* name = "gtest-MetaspaceArena") :
94 _context(helper)
95 {
96 initialize(growth_policy, name);
97 }
98
~MetaspaceArenaTestHelper()99 ~MetaspaceArenaTestHelper() {
100 delete_arena_with_tests();
101 delete _lock;
102 }
103
limiter() const104 const CommitLimiter& limiter() const { return _context.commit_limiter(); }
arena() const105 MetaspaceArena* arena() const { return _arena; }
used_words_counter()106 SizeAtomicCounter& used_words_counter() { return _used_words_counter; }
107
108 // Note: all test functions return void due to gtests limitation that we cannot use ASSERT
109 // in non-void returning tests.
110
delete_arena_with_tests()111 void delete_arena_with_tests() {
112 if (_arena != NULL) {
113 size_t used_words_before = _used_words_counter.get();
114 size_t committed_words_before = limiter().committed_words();
115 DEBUG_ONLY(_arena->verify());
116 delete _arena;
117 _arena = NULL;
118 size_t used_words_after = _used_words_counter.get();
119 size_t committed_words_after = limiter().committed_words();
120 ASSERT_0(used_words_after);
121 if (Settings::uncommit_free_chunks()) {
122 ASSERT_LE(committed_words_after, committed_words_before);
123 } else {
124 ASSERT_EQ(committed_words_after, committed_words_before);
125 }
126 }
127 }
128
usage_numbers_with_test(size_t * p_used,size_t * p_committed,size_t * p_capacity) const129 void usage_numbers_with_test(size_t* p_used, size_t* p_committed, size_t* p_capacity) const {
130 _arena->usage_numbers(p_used, p_committed, p_capacity);
131 if (p_used != NULL) {
132 if (p_committed != NULL) {
133 ASSERT_GE(*p_committed, *p_used);
134 }
135 // Since we own the used words counter, it should reflect our usage number 1:1
136 ASSERT_EQ(_used_words_counter.get(), *p_used);
137 }
138 if (p_committed != NULL && p_capacity != NULL) {
139 ASSERT_GE(*p_capacity, *p_committed);
140 }
141 }
142
143 // Allocate; caller expects success; return pointer in *p_return_value
allocate_from_arena_with_tests_expect_success(MetaWord ** p_return_value,size_t word_size)144 void allocate_from_arena_with_tests_expect_success(MetaWord** p_return_value, size_t word_size) {
145 allocate_from_arena_with_tests(p_return_value, word_size);
146 ASSERT_NOT_NULL(*p_return_value);
147 }
148
149 // Allocate; caller expects success but is not interested in return value
allocate_from_arena_with_tests_expect_success(size_t word_size)150 void allocate_from_arena_with_tests_expect_success(size_t word_size) {
151 MetaWord* dummy = NULL;
152 allocate_from_arena_with_tests_expect_success(&dummy, word_size);
153 }
154
155 // Allocate; caller expects failure
allocate_from_arena_with_tests_expect_failure(size_t word_size)156 void allocate_from_arena_with_tests_expect_failure(size_t word_size) {
157 MetaWord* dummy = NULL;
158 allocate_from_arena_with_tests(&dummy, word_size);
159 ASSERT_NULL(dummy);
160 }
161
162 // Allocate; it may or may not work; return value in *p_return_value
allocate_from_arena_with_tests(MetaWord ** p_return_value,size_t word_size)163 void allocate_from_arena_with_tests(MetaWord** p_return_value, size_t word_size) {
164
165 // Note: usage_numbers walks all chunks in use and counts.
166 size_t used = 0, committed = 0, capacity = 0;
167 usage_numbers_with_test(&used, &committed, &capacity);
168
169 size_t possible_expansion = limiter().possible_expansion_words();
170
171 MetaWord* p = _arena->allocate(word_size);
172
173 SOMETIMES(DEBUG_ONLY(_arena->verify();))
174
175 size_t used2 = 0, committed2 = 0, capacity2 = 0;
176 usage_numbers_with_test(&used2, &committed2, &capacity2);
177
178 if (p == NULL) {
179 // Allocation failed.
180 if (Settings::new_chunks_are_fully_committed()) {
181 ASSERT_LT(possible_expansion, MAX_CHUNK_WORD_SIZE);
182 } else {
183 ASSERT_LT(possible_expansion, word_size);
184 }
185
186 ASSERT_EQ(used, used2);
187 ASSERT_EQ(committed, committed2);
188 ASSERT_EQ(capacity, capacity2);
189 } else {
190 // Allocation succeeded. Should be correctly aligned.
191 ASSERT_TRUE(is_aligned(p, sizeof(MetaWord)));
192 // used: may go up or may not (since our request may have been satisfied from the freeblocklist
193 // whose content already counts as used).
194 // committed: may go up, may not
195 // capacity: ditto
196 ASSERT_GE(used2, used);
197 ASSERT_GE(committed2, committed);
198 ASSERT_GE(capacity2, capacity);
199 }
200
201 *p_return_value = p;
202 }
203
204 // Allocate; it may or may not work; but caller does not care for the result value
allocate_from_arena_with_tests(size_t word_size)205 void allocate_from_arena_with_tests(size_t word_size) {
206 MetaWord* dummy = NULL;
207 allocate_from_arena_with_tests(&dummy, word_size);
208 }
209
deallocate_with_tests(MetaWord * p,size_t word_size)210 void deallocate_with_tests(MetaWord* p, size_t word_size) {
211 size_t used = 0, committed = 0, capacity = 0;
212 usage_numbers_with_test(&used, &committed, &capacity);
213
214 _arena->deallocate(p, word_size);
215
216 SOMETIMES(DEBUG_ONLY(_arena->verify();))
217
218 size_t used2 = 0, committed2 = 0, capacity2 = 0;
219 usage_numbers_with_test(&used2, &committed2, &capacity2);
220
221 // Nothing should have changed. Deallocated blocks are added to the free block list
222 // which still counts as used.
223 ASSERT_EQ(used2, used);
224 ASSERT_EQ(committed2, committed);
225 ASSERT_EQ(capacity2, capacity);
226 }
227
get_arena_statistics() const228 ArenaStats get_arena_statistics() const {
229 ArenaStats stats;
230 _arena->add_to_statistics(&stats);
231 return stats;
232 }
233
234 // Convenience method to return number of chunks in arena (including current chunk)
get_number_of_chunks() const235 int get_number_of_chunks() const {
236 return get_arena_statistics().totals()._num;
237 }
238
239 };
240
test_basics(size_t commit_limit,bool is_micro)241 static void test_basics(size_t commit_limit, bool is_micro) {
242 MetaspaceGtestContext context(commit_limit);
243 MetaspaceArenaTestHelper helper(context, is_micro ? Metaspace::ReflectionMetaspaceType : Metaspace::StandardMetaspaceType, false);
244
245 helper.allocate_from_arena_with_tests(1);
246 helper.allocate_from_arena_with_tests(128);
247 helper.allocate_from_arena_with_tests(128 * K);
248 helper.allocate_from_arena_with_tests(1);
249 helper.allocate_from_arena_with_tests(128);
250 helper.allocate_from_arena_with_tests(128 * K);
251 }
252
TEST_VM(metaspace,MetaspaceArena_basics_micro_nolimit)253 TEST_VM(metaspace, MetaspaceArena_basics_micro_nolimit) {
254 test_basics(max_uintx, true);
255 }
256
TEST_VM(metaspace,MetaspaceArena_basics_micro_limit)257 TEST_VM(metaspace, MetaspaceArena_basics_micro_limit) {
258 test_basics(256 * K, true);
259 }
260
TEST_VM(metaspace,MetaspaceArena_basics_standard_nolimit)261 TEST_VM(metaspace, MetaspaceArena_basics_standard_nolimit) {
262 test_basics(max_uintx, false);
263 }
264
TEST_VM(metaspace,MetaspaceArena_basics_standard_limit)265 TEST_VM(metaspace, MetaspaceArena_basics_standard_limit) {
266 test_basics(256 * K, false);
267 }
268
269 // Test chunk enlargement:
270 // A single MetaspaceArena, left undisturbed with place to grow. Slowly fill arena up.
271 // We should see at least some occurrences of chunk-in-place enlargement.
test_chunk_enlargment_simple(Metaspace::MetaspaceType spacetype,bool is_class)272 static void test_chunk_enlargment_simple(Metaspace::MetaspaceType spacetype, bool is_class) {
273
274 MetaspaceGtestContext context;
275 MetaspaceArenaTestHelper helper(context, (Metaspace::MetaspaceType)spacetype, is_class);
276
277 uint64_t n1 = metaspace::InternalStats::num_chunks_enlarged();
278
279 size_t allocated = 0;
280 while (allocated <= MAX_CHUNK_WORD_SIZE &&
281 metaspace::InternalStats::num_chunks_enlarged() == n1) {
282 size_t s = IntRange(32, 128).random_value();
283 helper.allocate_from_arena_with_tests_expect_success(s);
284 allocated += metaspace::get_raw_word_size_for_requested_word_size(s);
285 }
286
287 EXPECT_GT(metaspace::InternalStats::num_chunks_enlarged(), n1);
288
289 }
290
291 // Do this test for some of the standard types; don't do it for the boot loader type
292 // since that one starts out with max chunk size so we would not see any enlargement.
293
TEST_VM(metaspace,MetaspaceArena_test_enlarge_in_place_standard_c)294 TEST_VM(metaspace, MetaspaceArena_test_enlarge_in_place_standard_c) {
295 test_chunk_enlargment_simple(Metaspace::StandardMetaspaceType, true);
296 }
297
TEST_VM(metaspace,MetaspaceArena_test_enlarge_in_place_standard_nc)298 TEST_VM(metaspace, MetaspaceArena_test_enlarge_in_place_standard_nc) {
299 test_chunk_enlargment_simple(Metaspace::StandardMetaspaceType, false);
300 }
301
TEST_VM(metaspace,MetaspaceArena_test_enlarge_in_place_micro_c)302 TEST_VM(metaspace, MetaspaceArena_test_enlarge_in_place_micro_c) {
303 test_chunk_enlargment_simple(Metaspace::ReflectionMetaspaceType, true);
304 }
305
TEST_VM(metaspace,MetaspaceArena_test_enlarge_in_place_micro_nc)306 TEST_VM(metaspace, MetaspaceArena_test_enlarge_in_place_micro_nc) {
307 test_chunk_enlargment_simple(Metaspace::ReflectionMetaspaceType, false);
308 }
309
310 // Test chunk enlargement:
311 // A single MetaspaceArena, left undisturbed with place to grow. Slowly fill arena up.
312 // We should see occurrences of chunk-in-place enlargement.
313 // Here, we give it an ideal policy which should enable the initial chunk to grow unmolested
314 // until finish.
TEST_VM(metaspace,MetaspaceArena_test_enlarge_in_place_2)315 TEST_VM(metaspace, MetaspaceArena_test_enlarge_in_place_2) {
316
317 if (Settings::use_allocation_guard()) {
318 return;
319 }
320
321 // Note: internally, chunk in-place enlargement is disallowed if growing the chunk
322 // would cause the arena to claim more memory than its growth policy allows. This
323 // is done to prevent the arena to grow too fast.
324 //
325 // In order to test in-place growth here without that restriction I give it an
326 // artificial growth policy which starts out with a tiny chunk size, then balloons
327 // right up to max chunk size. This will cause the initial chunk to be tiny, and
328 // then the arena is able to grow it without violating growth policy.
329 chunklevel_t growth[] = { HIGHEST_CHUNK_LEVEL, ROOT_CHUNK_LEVEL };
330 ArenaGrowthPolicy growth_policy(growth, 2);
331
332 MetaspaceGtestContext context;
333 MetaspaceArenaTestHelper helper(context, &growth_policy);
334
335 uint64_t n1 = metaspace::InternalStats::num_chunks_enlarged();
336
337 size_t allocated = 0;
338 while (allocated <= MAX_CHUNK_WORD_SIZE) {
339 size_t s = IntRange(32, 128).random_value();
340 helper.allocate_from_arena_with_tests_expect_success(s);
341 allocated += metaspace::get_raw_word_size_for_requested_word_size(s);
342 if (allocated <= MAX_CHUNK_WORD_SIZE) {
343 // Chunk should have been enlarged in place
344 ASSERT_EQ(1, helper.get_number_of_chunks());
345 } else {
346 // Next chunk should have started
347 ASSERT_EQ(2, helper.get_number_of_chunks());
348 }
349 }
350
351 int times_chunk_were_enlarged = metaspace::InternalStats::num_chunks_enlarged() - n1;
352 LOG("chunk was enlarged %d times.", times_chunk_were_enlarged);
353
354 ASSERT_GT0(times_chunk_were_enlarged);
355
356 }
357
358 // Regression test: Given a single MetaspaceArena, left undisturbed with place to grow,
359 // test that in place enlargement correctly fails if growing the chunk would bring us
360 // beyond the max. size of a chunk.
TEST_VM(metaspace,MetaspaceArena_test_failing_to_enlarge_in_place_max_chunk_size)361 TEST_VM(metaspace, MetaspaceArena_test_failing_to_enlarge_in_place_max_chunk_size) {
362
363 if (Settings::use_allocation_guard()) {
364 return;
365 }
366
367 MetaspaceGtestContext context;
368
369 for (size_t first_allocation_size = 1; first_allocation_size <= MAX_CHUNK_WORD_SIZE / 2; first_allocation_size *= 2) {
370
371 MetaspaceArenaTestHelper helper(context, Metaspace::StandardMetaspaceType, false);
372
373 // we allocate first a small amount, then the full amount possible.
374 // The sum of first and second allocation should bring us above root chunk size.
375 // This should work, we should not see any problems, but no chunk enlargement should
376 // happen.
377 int n1 = metaspace::InternalStats::num_chunks_enlarged();
378
379 helper.allocate_from_arena_with_tests_expect_success(first_allocation_size);
380 EXPECT_EQ(helper.get_number_of_chunks(), 1);
381
382 helper.allocate_from_arena_with_tests_expect_success(MAX_CHUNK_WORD_SIZE - first_allocation_size + 1);
383 EXPECT_EQ(helper.get_number_of_chunks(), 2);
384
385 int times_chunk_were_enlarged = metaspace::InternalStats::num_chunks_enlarged() - n1;
386 LOG("chunk was enlarged %d times.", times_chunk_were_enlarged);
387
388 EXPECT_0(times_chunk_were_enlarged);
389
390 }
391 }
392
393 // Regression test: Given a single MetaspaceArena, left undisturbed with place to grow,
394 // test that in place enlargement correctly fails if growing the chunk would cause more
395 // than doubling its size
TEST_VM(metaspace,MetaspaceArena_test_failing_to_enlarge_in_place_doubling_chunk_size)396 TEST_VM(metaspace, MetaspaceArena_test_failing_to_enlarge_in_place_doubling_chunk_size) {
397
398 if (Settings::use_allocation_guard()) {
399 return;
400 }
401
402 MetaspaceGtestContext context;
403 MetaspaceArenaTestHelper helper(context, Metaspace::StandardMetaspaceType, false);
404
405 int n1 = metaspace::InternalStats::num_chunks_enlarged();
406
407 helper.allocate_from_arena_with_tests_expect_success(1000);
408 EXPECT_EQ(helper.get_number_of_chunks(), 1);
409
410 helper.allocate_from_arena_with_tests_expect_success(4000);
411 EXPECT_EQ(helper.get_number_of_chunks(), 2);
412
413 int times_chunk_were_enlarged = metaspace::InternalStats::num_chunks_enlarged() - n1;
414 LOG("chunk was enlarged %d times.", times_chunk_were_enlarged);
415
416 EXPECT_0(times_chunk_were_enlarged);
417
418 }
419
420 // Test the MetaspaceArenas' free block list:
421 // Allocate, deallocate, then allocate the same block again. The second allocate should
422 // reuse the deallocated block.
TEST_VM(metaspace,MetaspaceArena_deallocate)423 TEST_VM(metaspace, MetaspaceArena_deallocate) {
424 if (Settings::use_allocation_guard()) {
425 return;
426 }
427 for (size_t s = 2; s <= MAX_CHUNK_WORD_SIZE; s *= 2) {
428 MetaspaceGtestContext context;
429 MetaspaceArenaTestHelper helper(context, Metaspace::StandardMetaspaceType, false);
430
431 MetaWord* p1 = NULL;
432 helper.allocate_from_arena_with_tests_expect_success(&p1, s);
433
434 size_t used1 = 0, capacity1 = 0;
435 helper.usage_numbers_with_test(&used1, NULL, &capacity1);
436 ASSERT_EQ(used1, s);
437
438 helper.deallocate_with_tests(p1, s);
439
440 size_t used2 = 0, capacity2 = 0;
441 helper.usage_numbers_with_test(&used2, NULL, &capacity2);
442 ASSERT_EQ(used1, used2);
443 ASSERT_EQ(capacity2, capacity2);
444
445 MetaWord* p2 = NULL;
446 helper.allocate_from_arena_with_tests_expect_success(&p2, s);
447
448 size_t used3 = 0, capacity3 = 0;
449 helper.usage_numbers_with_test(&used3, NULL, &capacity3);
450 ASSERT_EQ(used3, used2);
451 ASSERT_EQ(capacity3, capacity2);
452
453 // Actually, we should get the very same allocation back
454 ASSERT_EQ(p1, p2);
455 }
456 }
457
test_recover_from_commit_limit_hit()458 static void test_recover_from_commit_limit_hit() {
459
460 if (Settings::new_chunks_are_fully_committed()) {
461 return; // This would throw off the commit counting in this test.
462 }
463
464 // Test:
465 // - Multiple MetaspaceArena allocate (operating under the same commit limiter).
466 // - One, while attempting to commit parts of its current chunk on demand,
467 // triggers the limit and cannot commit its chunk further.
468 // - We release the other MetaspaceArena - its content is put back to the
469 // freelists.
470 // - We re-attempt allocation from the first manager. It should now succeed.
471 //
472 // This means if the first MetaspaceArena may have to let go of its current chunk and
473 // retire it and take a fresh chunk from the freelist.
474
475 const size_t commit_limit = Settings::commit_granule_words() * 10;
476 MetaspaceGtestContext context(commit_limit);
477
478 // The first MetaspaceArena mimicks a micro loader. This will fill the free
479 // chunk list with very small chunks. We allocate from them in an interleaved
480 // way to cause fragmentation.
481 MetaspaceArenaTestHelper helper1(context, Metaspace::ReflectionMetaspaceType, false);
482 MetaspaceArenaTestHelper helper2(context, Metaspace::ReflectionMetaspaceType, false);
483
484 // This MetaspaceArena should hit the limit. We use BootMetaspaceType here since
485 // it gets a large initial chunk which is committed
486 // on demand and we are likely to hit a commit limit while trying to expand it.
487 MetaspaceArenaTestHelper helper3(context, Metaspace::BootMetaspaceType, false);
488
489 // Allocate space until we have below two but above one granule left
490 size_t allocated_from_1_and_2 = 0;
491 while (context.commit_limiter().possible_expansion_words() >= Settings::commit_granule_words() * 2 &&
492 allocated_from_1_and_2 < commit_limit) {
493 helper1.allocate_from_arena_with_tests_expect_success(1);
494 helper2.allocate_from_arena_with_tests_expect_success(1);
495 allocated_from_1_and_2 += 2;
496 }
497
498 // Now, allocating from helper3, creep up on the limit
499 size_t allocated_from_3 = 0;
500 MetaWord* p = NULL;
501 while ( (helper3.allocate_from_arena_with_tests(&p, 1), p != NULL) &&
502 ++allocated_from_3 < Settings::commit_granule_words() * 2);
503
504 EXPECT_LE(allocated_from_3, Settings::commit_granule_words() * 2);
505
506 // We expect the freelist to be empty of committed space...
507 EXPECT_0(context.cm().calc_committed_word_size());
508
509 //msthelper.cm().print_on(tty);
510
511 // Release the first MetaspaceArena.
512 helper1.delete_arena_with_tests();
513
514 //msthelper.cm().print_on(tty);
515
516 // Should have populated the freelist with committed space
517 // We expect the freelist to be empty of committed space...
518 EXPECT_GT(context.cm().calc_committed_word_size(), (size_t)0);
519
520 // Repeat allocation from helper3, should now work.
521 helper3.allocate_from_arena_with_tests_expect_success(1);
522
523 }
524
TEST_VM(metaspace,MetaspaceArena_recover_from_limit_hit)525 TEST_VM(metaspace, MetaspaceArena_recover_from_limit_hit) {
526 test_recover_from_commit_limit_hit();
527 }
528
test_controlled_growth(Metaspace::MetaspaceType type,bool is_class,size_t expected_starting_capacity,bool test_in_place_enlargement)529 static void test_controlled_growth(Metaspace::MetaspaceType type, bool is_class,
530 size_t expected_starting_capacity,
531 bool test_in_place_enlargement)
532 {
533
534 if (Settings::use_allocation_guard()) {
535 return;
536 }
537
538 // From a MetaspaceArena in a clean room allocate tiny amounts;
539 // watch it grow. Used/committed/capacity should not grow in
540 // large jumps. Also, different types of MetaspaceArena should
541 // have different initial capacities.
542
543 MetaspaceGtestContext context;
544 MetaspaceArenaTestHelper smhelper(context, type, is_class, "Grower");
545
546 MetaspaceArenaTestHelper smhelper_harrasser(context, Metaspace::ReflectionMetaspaceType, true, "Harasser");
547
548 size_t used = 0, committed = 0, capacity = 0;
549 const size_t alloc_words = 16;
550
551 smhelper.arena()->usage_numbers(&used, &committed, &capacity);
552 ASSERT_0(used);
553 ASSERT_0(committed);
554 ASSERT_0(capacity);
555
556 ///// First allocation //
557
558 smhelper.allocate_from_arena_with_tests_expect_success(alloc_words);
559
560 smhelper.arena()->usage_numbers(&used, &committed, &capacity);
561
562 ASSERT_EQ(used, alloc_words);
563 ASSERT_GE(committed, used);
564 ASSERT_GE(capacity, committed);
565
566 ASSERT_EQ(capacity, expected_starting_capacity);
567
568 if (!(Settings::new_chunks_are_fully_committed() && type == Metaspace::BootMetaspaceType)) {
569 // Initial commit charge for the whole context should be one granule
570 ASSERT_EQ(context.committed_words(), Settings::commit_granule_words());
571 // Initial commit number for the arena should be less since - apart from boot loader - no
572 // space type has large initial chunks.
573 ASSERT_LE(committed, Settings::commit_granule_words());
574 }
575
576 ///// subsequent allocations //
577
578 DEBUG_ONLY(const uintx num_chunk_enlarged = metaspace::InternalStats::num_chunks_enlarged();)
579
580 size_t words_allocated = 0;
581 int num_allocated = 0;
582 const size_t safety = MAX_CHUNK_WORD_SIZE * 1.2;
583 size_t highest_capacity_jump = capacity;
584 int num_capacity_jumps = 0;
585
586 while (words_allocated < safety && num_capacity_jumps < 15) {
587
588 // if we want to test growth with in-place chunk enlargement, leave MetaspaceArena
589 // undisturbed; it will have all the place to grow. Otherwise allocate from a little
590 // side arena to increase fragmentation.
591 // (Note that this does not completely prevent in-place chunk enlargement but makes it
592 // rather improbable)
593 if (!test_in_place_enlargement) {
594 smhelper_harrasser.allocate_from_arena_with_tests_expect_success(alloc_words * 2);
595 }
596
597 smhelper.allocate_from_arena_with_tests_expect_success(alloc_words);
598 words_allocated += metaspace::get_raw_word_size_for_requested_word_size(alloc_words);
599 num_allocated++;
600
601 size_t used2 = 0, committed2 = 0, capacity2 = 0;
602
603 smhelper.arena()->usage_numbers(&used2, &committed2, &capacity2);
604
605 // used should not grow larger than what we allocated, plus possible overhead.
606 ASSERT_GE(used2, used);
607 ASSERT_LE(used2, used + alloc_words * 2);
608 ASSERT_LE(used2, words_allocated + 100);
609 used = used2;
610
611 // A jump in committed words should not be larger than commit granule size.
612 // It can be smaller, since the current chunk of the MetaspaceArena may be
613 // smaller than a commit granule.
614 // (Note: unless root chunks are born fully committed)
615 ASSERT_GE(committed2, used2);
616 ASSERT_GE(committed2, committed);
617 const size_t committed_jump = committed2 - committed;
618 if (committed_jump > 0 && !Settings::new_chunks_are_fully_committed()) {
619 ASSERT_LE(committed_jump, Settings::commit_granule_words());
620 }
621 committed = committed2;
622
623 // Capacity jumps: Test that arenas capacity does not grow too fast.
624 ASSERT_GE(capacity2, committed2);
625 ASSERT_GE(capacity2, capacity);
626 const size_t capacity_jump = capacity2 - capacity;
627 if (capacity_jump > 0) {
628 LOG(">" SIZE_FORMAT "->" SIZE_FORMAT "(+" SIZE_FORMAT ")", capacity, capacity2, capacity_jump)
629 if (capacity_jump > highest_capacity_jump) {
630 /* Disabled for now since this is rather shaky. The way it is tested makes it too dependent
631 * on allocation history. Need to rethink this.
632 ASSERT_LE(capacity_jump, highest_capacity_jump * 2);
633 ASSERT_GE(capacity_jump, MIN_CHUNK_WORD_SIZE);
634 ASSERT_LE(capacity_jump, MAX_CHUNK_WORD_SIZE);
635 */
636 highest_capacity_jump = capacity_jump;
637 }
638 num_capacity_jumps++;
639 }
640
641 capacity = capacity2;
642
643 }
644
645 // After all this work, we should see an increase in number of chunk-in-place-enlargements
646 // (this especially is vulnerable to regression: the decisions of when to do in-place-enlargements are somewhat
647 // complicated, see MetaspaceArena::attempt_enlarge_current_chunk())
648 #ifdef ASSERT
649 if (test_in_place_enlargement) {
650 const uintx num_chunk_enlarged_2 = metaspace::InternalStats::num_chunks_enlarged();
651 ASSERT_GT(num_chunk_enlarged_2, num_chunk_enlarged);
652 }
653 #endif
654 }
655
656 // these numbers have to be in sync with arena policy numbers (see memory/metaspace/arenaGrowthPolicy.cpp)
TEST_VM(metaspace,MetaspaceArena_growth_refl_c_inplace)657 TEST_VM(metaspace, MetaspaceArena_growth_refl_c_inplace) {
658 test_controlled_growth(Metaspace::ReflectionMetaspaceType, true,
659 word_size_for_level(CHUNK_LEVEL_1K), true);
660 }
661
TEST_VM(metaspace,MetaspaceArena_growth_refl_c_not_inplace)662 TEST_VM(metaspace, MetaspaceArena_growth_refl_c_not_inplace) {
663 test_controlled_growth(Metaspace::ReflectionMetaspaceType, true,
664 word_size_for_level(CHUNK_LEVEL_1K), false);
665 }
666
TEST_VM(metaspace,MetaspaceArena_growth_anon_c_inplace)667 TEST_VM(metaspace, MetaspaceArena_growth_anon_c_inplace) {
668 test_controlled_growth(Metaspace::ClassMirrorHolderMetaspaceType, true,
669 word_size_for_level(CHUNK_LEVEL_1K), true);
670 }
671
TEST_VM(metaspace,MetaspaceArena_growth_anon_c_not_inplace)672 TEST_VM(metaspace, MetaspaceArena_growth_anon_c_not_inplace) {
673 test_controlled_growth(Metaspace::ClassMirrorHolderMetaspaceType, true,
674 word_size_for_level(CHUNK_LEVEL_1K), false);
675 }
676
TEST_VM(metaspace,MetaspaceArena_growth_standard_c_inplace)677 TEST_VM(metaspace, MetaspaceArena_growth_standard_c_inplace) {
678 test_controlled_growth(Metaspace::StandardMetaspaceType, true,
679 word_size_for_level(CHUNK_LEVEL_2K), true);
680 }
681
TEST_VM(metaspace,MetaspaceArena_growth_standard_c_not_inplace)682 TEST_VM(metaspace, MetaspaceArena_growth_standard_c_not_inplace) {
683 test_controlled_growth(Metaspace::StandardMetaspaceType, true,
684 word_size_for_level(CHUNK_LEVEL_2K), false);
685 }
686
687 /* Disabled growth tests for BootMetaspaceType: there, the growth steps are too rare,
688 * and too large, to make any reliable guess as toward chunks get enlarged in place.
689 TEST_VM(metaspace, MetaspaceArena_growth_boot_c_inplace) {
690 test_controlled_growth(Metaspace::BootMetaspaceType, true,
691 word_size_for_level(CHUNK_LEVEL_1M), true);
692 }
693
694 TEST_VM(metaspace, MetaspaceArena_growth_boot_c_not_inplace) {
695 test_controlled_growth(Metaspace::BootMetaspaceType, true,
696 word_size_for_level(CHUNK_LEVEL_1M), false);
697 }
698 */
699
TEST_VM(metaspace,MetaspaceArena_growth_refl_nc_inplace)700 TEST_VM(metaspace, MetaspaceArena_growth_refl_nc_inplace) {
701 test_controlled_growth(Metaspace::ReflectionMetaspaceType, false,
702 word_size_for_level(CHUNK_LEVEL_2K), true);
703 }
704
TEST_VM(metaspace,MetaspaceArena_growth_refl_nc_not_inplace)705 TEST_VM(metaspace, MetaspaceArena_growth_refl_nc_not_inplace) {
706 test_controlled_growth(Metaspace::ReflectionMetaspaceType, false,
707 word_size_for_level(CHUNK_LEVEL_2K), false);
708 }
709
TEST_VM(metaspace,MetaspaceArena_growth_anon_nc_inplace)710 TEST_VM(metaspace, MetaspaceArena_growth_anon_nc_inplace) {
711 test_controlled_growth(Metaspace::ClassMirrorHolderMetaspaceType, false,
712 word_size_for_level(CHUNK_LEVEL_1K), true);
713 }
714
TEST_VM(metaspace,MetaspaceArena_growth_anon_nc_not_inplace)715 TEST_VM(metaspace, MetaspaceArena_growth_anon_nc_not_inplace) {
716 test_controlled_growth(Metaspace::ClassMirrorHolderMetaspaceType, false,
717 word_size_for_level(CHUNK_LEVEL_1K), false);
718 }
719
TEST_VM(metaspace,MetaspaceArena_growth_standard_nc_inplace)720 TEST_VM(metaspace, MetaspaceArena_growth_standard_nc_inplace) {
721 test_controlled_growth(Metaspace::StandardMetaspaceType, false,
722 word_size_for_level(CHUNK_LEVEL_4K), true);
723 }
724
TEST_VM(metaspace,MetaspaceArena_growth_standard_nc_not_inplace)725 TEST_VM(metaspace, MetaspaceArena_growth_standard_nc_not_inplace) {
726 test_controlled_growth(Metaspace::StandardMetaspaceType, false,
727 word_size_for_level(CHUNK_LEVEL_4K), false);
728 }
729
730 /* Disabled growth tests for BootMetaspaceType: there, the growth steps are too rare,
731 * and too large, to make any reliable guess as toward chunks get enlarged in place.
732 TEST_VM(metaspace, MetaspaceArena_growth_boot_nc_inplace) {
733 test_controlled_growth(Metaspace::BootMetaspaceType, false,
734 word_size_for_level(CHUNK_LEVEL_4M), true);
735 }
736
737 TEST_VM(metaspace, MetaspaceArena_growth_boot_nc_not_inplace) {
738 test_controlled_growth(Metaspace::BootMetaspaceType, false,
739 word_size_for_level(CHUNK_LEVEL_4M), false);
740 }
741 */
742