jdk

Форк
0
/
test_metaspacearena_stress.cpp 
435 строк · 13.9 Кб
1
/*
2
 * Copyright (c) 2020, 2024, 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::AllocationAlignmentByteSize;
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
  MetaspaceArena* _arena;
61

62
  const SizeRange _allocation_range;
63
  size_t _size_of_last_failed_allocation;
64

65
  // We keep track of all allocations done thru the MetaspaceArena to
66
  // later check for overwriters.
67
  struct allocation_t {
68
    allocation_t* next;
69
    MetaWord* p; // nullptr if deallocated
70
    size_t word_size;
71
    void mark() {
72
      mark_range(p, word_size);
73
    }
74
    void verify() const {
75
      if (p != nullptr) {
76
        check_marked_range(p, word_size);
77
      }
78
    }
79
  };
80

81
  allocation_t* _allocations;
82

83
  // We count how much we did allocate and deallocate
84
  MemRangeCounter _alloc_count;
85
  MemRangeCounter _dealloc_count;
86

87
  // Check statistics returned by MetaspaceArena::add_to_statistics() against what
88
  // we know we allocated. This is a bit flaky since MetaspaceArena has internal
89
  // overhead.
90
  void verify_arena_statistics() const {
91

92
    ArenaStats stats;
93
    _arena->add_to_statistics(&stats);
94
    InUseChunkStats in_use_stats = stats.totals();
95

96
    assert(_dealloc_count.total_size() <= _alloc_count.total_size() &&
97
           _dealloc_count.count() <= _alloc_count.count(), "Sanity");
98

99
    // Check consistency of stats
100
    ASSERT_GE(in_use_stats._word_size, in_use_stats._committed_words);
101
    ASSERT_EQ(in_use_stats._committed_words,
102
              in_use_stats._used_words + in_use_stats._free_words + in_use_stats._waste_words);
103
    ASSERT_GE(in_use_stats._used_words, stats._free_blocks_word_size);
104

105
    // Note: reasons why the outside alloc counter and the inside used counter can differ:
106
    // - alignment/padding of allocations
107
    // - inside used counter contains blocks in free list
108
    // - free block list splinter threshold
109

110
    // Since what we deallocated may have been given back to us in a following allocation,
111
    // we only know fore sure we allocated what we did not give back.
112
    const size_t at_least_allocated = _alloc_count.total_size() - _dealloc_count.total_size();
113

114
    // At most we allocated this:
115
    constexpr size_t max_word_overhead_per_alloc = 4;
116
    const size_t at_most_allocated = _alloc_count.total_size() + max_word_overhead_per_alloc * _alloc_count.count();
117

118
    ASSERT_LE(at_least_allocated, in_use_stats._used_words - stats._free_blocks_word_size);
119
    ASSERT_GE(at_most_allocated, in_use_stats._used_words - stats._free_blocks_word_size);
120

121
  }
122

123
public:
124

125
  MetaspaceArena* arena() { return _arena; }
126

127
  MetaspaceArenaTestBed(ChunkManager* cm, const ArenaGrowthPolicy* alloc_sequence,
128
                        SizeAtomicCounter* used_words_counter, SizeRange allocation_range) :
129
    _arena(nullptr),
130
    _allocation_range(allocation_range),
131
    _size_of_last_failed_allocation(0),
132
    _allocations(nullptr),
133
    _alloc_count(),
134
    _dealloc_count()
135
  {
136
    _arena = new MetaspaceArena(cm, alloc_sequence, used_words_counter, "gtest-MetaspaceArenaTestBed-sm");
137
  }
138

139
  ~MetaspaceArenaTestBed() {
140

141
    verify_arena_statistics();
142

143
    allocation_t* a = _allocations;
144
    while (a != nullptr) {
145
      allocation_t* b = a->next;
146
      a->verify();
147
      FREE_C_HEAP_OBJ(a);
148
      a = b;
149
    }
150

151
    DEBUG_ONLY(_arena->verify();)
152

153
    // Delete MetaspaceArena. That should clean up all metaspace.
154
    delete _arena;
155

156
  }
157

158
  size_t words_allocated() const        { return _alloc_count.total_size(); }
159
  int num_allocations() const           { return _alloc_count.count(); }
160

161
  size_t size_of_last_failed_allocation() const { return _size_of_last_failed_allocation; }
162

163
  // Allocate a random amount. Return false if the allocation failed.
164
  bool checked_random_allocate() {
165
    size_t word_size = 1 + _allocation_range.random_value();
166
    MetaWord* p = _arena->allocate(word_size);
167
    if (p != nullptr) {
168
      EXPECT_TRUE(is_aligned(p, AllocationAlignmentByteSize));
169

170
      allocation_t* a = NEW_C_HEAP_OBJ(allocation_t, mtInternal);
171
      a->word_size = word_size;
172
      a->p = p;
173
      a->mark();
174
      a->next = _allocations;
175
      _allocations = a;
176
      _alloc_count.add(word_size);
177
      if ((_alloc_count.count() % 20) == 0) {
178
        verify_arena_statistics();
179
        DEBUG_ONLY(_arena->verify();)
180
      }
181
      return true;
182
    } else {
183
      _size_of_last_failed_allocation = word_size;
184
    }
185
    return false;
186
  }
187

188
  // Deallocate a random allocation
189
  void checked_random_deallocate() {
190
    allocation_t* a = _allocations;
191
    while (a && a->p != nullptr && os::random() % 10 != 0) {
192
      a = a->next;
193
    }
194
    if (a != nullptr && a->p != nullptr) {
195
      a->verify();
196
      _arena->deallocate(a->p, a->word_size);
197
      _dealloc_count.add(a->word_size);
198
      a->p = nullptr; a->word_size = 0;
199
      if ((_dealloc_count.count() % 20) == 0) {
200
        verify_arena_statistics();
201
        DEBUG_ONLY(_arena->verify();)
202
      }
203
    }
204
  }
205

206
}; // End: MetaspaceArenaTestBed
207

208
class MetaspaceArenaTest {
209

210
  MetaspaceGtestContext _context;
211

212
  SizeAtomicCounter _used_words_counter;
213

214
  SparseArray<MetaspaceArenaTestBed*> _testbeds;
215
  IntCounter _num_beds;
216

217
  //////// Bed creation, destruction ///////
218

219
  void create_new_test_bed_at(int slotindex, const ArenaGrowthPolicy* growth_policy, SizeRange allocation_range) {
220
    DEBUG_ONLY(_testbeds.check_slot_is_null(slotindex));
221
    MetaspaceArenaTestBed* bed = new MetaspaceArenaTestBed(&_context.cm(), growth_policy,
222
                                                       &_used_words_counter, allocation_range);
223
    _testbeds.set_at(slotindex, bed);
224
    _num_beds.increment();
225
  }
226

227
  void create_random_test_bed_at(int slotindex) {
228
    SizeRange allocation_range(1, 100); // randomize too?
229
    const ArenaGrowthPolicy* growth_policy = ArenaGrowthPolicy::policy_for_space_type(
230
        (fifty_fifty() ? Metaspace::StandardMetaspaceType : Metaspace::ReflectionMetaspaceType),
231
         fifty_fifty());
232
    create_new_test_bed_at(slotindex, growth_policy, allocation_range);
233
   }
234

235
  // Randomly create a random test bed at a random slot, and return its slot index
236
  // (returns false if we reached max number of test beds)
237
  bool create_random_test_bed() {
238
    const int slot = _testbeds.random_null_slot_index();
239
    if (slot != -1) {
240
      create_random_test_bed_at(slot);
241
    }
242
    return slot;
243
  }
244

245
  // Create test beds for all slots
246
  void create_all_test_beds() {
247
    for (int slot = 0; slot < _testbeds.size(); slot++) {
248
      if (_testbeds.slot_is_null(slot)) {
249
        create_random_test_bed_at(slot);
250
      }
251
    }
252
  }
253

254
  void delete_test_bed_at(int slotindex) {
255
    DEBUG_ONLY(_testbeds.check_slot_is_not_null(slotindex));
256
    MetaspaceArenaTestBed* bed = _testbeds.at(slotindex);
257
    delete bed; // This will return all its memory to the chunk manager
258
    _testbeds.set_at(slotindex, nullptr);
259
    _num_beds.decrement();
260
  }
261

262
  // Randomly delete a random test bed at a random slot
263
  // Return false if there are no test beds to delete.
264
  bool delete_random_test_bed() {
265
    const int slotindex = _testbeds.random_non_null_slot_index();
266
    if (slotindex != -1) {
267
      delete_test_bed_at(slotindex);
268
      return true;
269
    }
270
    return false;
271
  }
272

273
  // Delete all test beds.
274
  void delete_all_test_beds() {
275
    for (int slot = _testbeds.first_non_null_slot(); slot != -1; slot = _testbeds.next_non_null_slot(slot)) {
276
      delete_test_bed_at(slot);
277
    }
278
  }
279

280
  //////// Allocating metaspace from test beds ///////
281

282
  bool random_allocate_from_testbed(int slotindex) {
283
    DEBUG_ONLY(_testbeds.check_slot_is_not_null(slotindex);)
284
    MetaspaceArenaTestBed* bed = _testbeds.at(slotindex);
285
    bool success = bed->checked_random_allocate();
286
    if (success == false) {
287
      // We must have hit a limit.
288
      EXPECT_LT(_context.commit_limiter().possible_expansion_words(),
289
                metaspace::get_raw_word_size_for_requested_word_size(bed->size_of_last_failed_allocation()));
290
    }
291
    return success;
292
  }
293

294
  // Allocate multiple times random sizes from a single MetaspaceArena.
295
  bool random_allocate_multiple_times_from_testbed(int slotindex, int num_allocations) {
296
    bool success = true;
297
    int n = 0;
298
    while (success && n < num_allocations) {
299
      success = random_allocate_from_testbed(slotindex);
300
      n++;
301
    }
302
    return success;
303
  }
304

305
  // Allocate multiple times random sizes from a single random MetaspaceArena.
306
  bool random_allocate_random_times_from_random_testbed() {
307
    int slot = _testbeds.random_non_null_slot_index();
308
    bool success = false;
309
    if (slot != -1) {
310
      const int n = IntRange(5, 20).random_value();
311
      success = random_allocate_multiple_times_from_testbed(slot, n);
312
    }
313
    return success;
314
  }
315

316
  /////// Deallocating from testbed ///////////////////
317

318
  void deallocate_from_testbed(int slotindex) {
319
    DEBUG_ONLY(_testbeds.check_slot_is_not_null(slotindex);)
320
    MetaspaceArenaTestBed* bed = _testbeds.at(slotindex);
321
    bed->checked_random_deallocate();
322
  }
323

324
  void deallocate_from_random_testbed() {
325
    int slot = _testbeds.random_non_null_slot_index();
326
    if (slot != -1) {
327
      deallocate_from_testbed(slot);
328
    }
329
  }
330

331
  /////// Stats ///////////////////////////////////////
332

333
  int get_total_number_of_allocations() const {
334
    int sum = 0;
335
    for (int i = _testbeds.first_non_null_slot(); i != -1; i = _testbeds.next_non_null_slot(i)) {
336
      sum += _testbeds.at(i)->num_allocations();
337
    }
338
    return sum;
339
  }
340

341
  size_t get_total_words_allocated() const {
342
    size_t sum = 0;
343
    for (int i = _testbeds.first_non_null_slot(); i != -1; i = _testbeds.next_non_null_slot(i)) {
344
      sum += _testbeds.at(i)->words_allocated();
345
    }
346
    return sum;
347
  }
348

349
public:
350

351
  MetaspaceArenaTest(size_t commit_limit, int num_testbeds)
352
    : _context(commit_limit),
353
      _testbeds(num_testbeds),
354
      _num_beds()
355
  {}
356

357
  ~MetaspaceArenaTest () {
358

359
    delete_all_test_beds();
360

361
  }
362

363
  //////////////// Tests ////////////////////////
364

365
  void test() {
366

367
    // In a big loop, randomly chose one of these actions
368
    // - creating a test bed (simulates a new loader creation)
369
    // - allocating from a test bed (simulates allocating metaspace for a loader)
370
    // - (rarely) deallocate (simulates metaspace deallocation, e.g. class redefinitions)
371
    // - delete a test bed (simulates collection of a loader and subsequent return of metaspace to freelists)
372

373
    const int iterations = 2500;
374

375
    // Lets have a ceiling on number of words allocated (this is independent from the commit limit)
376
    const size_t max_allocation_size = 8 * M;
377

378
    bool force_bed_deletion = false;
379

380
    for (int niter = 0; niter < iterations; niter++) {
381

382
      const int r = IntRange(100).random_value();
383

384
      if (force_bed_deletion || r < 10) {
385

386
        force_bed_deletion = false;
387
        delete_random_test_bed();
388

389
      } else if (r < 20 || _num_beds.get() < (unsigned)_testbeds.size() / 2) {
390

391
        create_random_test_bed();
392

393
      } else if (r < 95) {
394

395
        // If allocation fails, we hit the commit limit and should delete some beds first
396
        force_bed_deletion = ! random_allocate_random_times_from_random_testbed();
397

398
      } else {
399

400
        // Note: does not affect the used words counter.
401
        deallocate_from_random_testbed();
402

403
      }
404

405
      // If we are close to our quota, start bed deletion
406
      if (_used_words_counter.get() >= max_allocation_size) {
407

408
        force_bed_deletion = true;
409

410
      }
411

412
    }
413

414
  }
415

416
};
417

418
// 32 parallel MetaspaceArena objects, random allocating without commit limit
419
TEST_VM(metaspace, MetaspaceArena_random_allocs_32_beds_no_commit_limit) {
420
  MetaspaceArenaTest test(max_uintx, 32);
421
  test.test();
422
}
423

424
// 32 parallel Metaspace arena objects, random allocating with commit limit
425
TEST_VM(metaspace, MetaspaceArena_random_allocs_32_beds_with_commit_limit) {
426
  MetaspaceArenaTest test(2 * M, 32);
427
  test.test();
428
}
429

430
// A single MetaspaceArena, random allocating without commit limit. This should exercise
431
//  chunk enlargement since allocation is undisturbed.
432
TEST_VM(metaspace, MetaspaceArena_random_allocs_1_bed_no_commit_limit) {
433
  MetaspaceArenaTest test(max_uintx, 1);
434
  test.test();
435
}
436

437

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.