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