Path: blob/master/test/hotspot/gtest/metaspace/test_metaspacearena_stress.cpp
41145 views
/*1* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.2* Copyright (c) 2020 SAP SE. All rights reserved.3* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.4*5* This code is free software; you can redistribute it and/or modify it6* under the terms of the GNU General Public License version 2 only, as7* published by the Free Software Foundation.8*9* This code is distributed in the hope that it will be useful, but WITHOUT10* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or11* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License12* version 2 for more details (a copy is included in the LICENSE file that13* accompanied this code).14*15* You should have received a copy of the GNU General Public License version16* 2 along with this work; if not, write to the Free Software Foundation,17* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.18*19* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA20* or visit www.oracle.com if you need additional information or have any21* questions.22*23*/2425#include "precompiled.hpp"26#include "memory/metaspace/chunkManager.hpp"27#include "memory/metaspace/counters.hpp"28#include "memory/metaspace/metaspaceArena.hpp"29#include "memory/metaspace/metaspaceArenaGrowthPolicy.hpp"30#include "memory/metaspace/metaspaceStatistics.hpp"31#include "runtime/mutexLocker.hpp"32#include "utilities/debug.hpp"33#include "utilities/globalDefinitions.hpp"34//#define LOG_PLEASE35#include "metaspaceGtestCommon.hpp"36#include "metaspaceGtestContexts.hpp"37#include "metaspaceGtestSparseArray.hpp"3839using metaspace::ArenaGrowthPolicy;40using metaspace::ChunkManager;41using metaspace::IntCounter;42using metaspace::MemRangeCounter;43using metaspace::MetaspaceArena;44using metaspace::SizeAtomicCounter;45using metaspace::ArenaStats;46using metaspace::InUseChunkStats;4748// Little randomness helper49static bool fifty_fifty() {50return IntRange(100).random_value() < 50;51}5253// See metaspaceArena.cpp : needed for predicting commit sizes.54namespace metaspace {55extern size_t get_raw_word_size_for_requested_word_size(size_t net_word_size);56}5758// A MetaspaceArenaTestBed contains a single MetaspaceArena and its lock.59// It keeps track of allocations done from this MetaspaceArena.60class MetaspaceArenaTestBed : public CHeapObj<mtInternal> {6162MetaspaceArena* _arena;6364Mutex* _lock;6566const SizeRange _allocation_range;67size_t _size_of_last_failed_allocation;6869// We keep track of all allocations done thru the MetaspaceArena to70// later check for overwriters.71struct allocation_t {72allocation_t* next;73MetaWord* p; // NULL if deallocated74size_t word_size;75void mark() {76mark_range(p, word_size);77}78void verify() const {79if (p != NULL) {80check_marked_range(p, word_size);81}82}83};8485allocation_t* _allocations;8687// We count how much we did allocate and deallocate88MemRangeCounter _alloc_count;89MemRangeCounter _dealloc_count;9091// Check statistics returned by MetaspaceArena::add_to_statistics() against what92// we know we allocated. This is a bit flaky since MetaspaceArena has internal93// overhead.94void verify_arena_statistics() const {9596ArenaStats stats;97_arena->add_to_statistics(&stats);98InUseChunkStats in_use_stats = stats.totals();99100assert(_dealloc_count.total_size() <= _alloc_count.total_size() &&101_dealloc_count.count() <= _alloc_count.count(), "Sanity");102103// Check consistency of stats104ASSERT_GE(in_use_stats._word_size, in_use_stats._committed_words);105ASSERT_EQ(in_use_stats._committed_words,106in_use_stats._used_words + in_use_stats._free_words + in_use_stats._waste_words);107ASSERT_GE(in_use_stats._used_words, stats._free_blocks_word_size);108109// Note: reasons why the outside alloc counter and the inside used counter can differ:110// - alignment/padding of allocations111// - inside used counter contains blocks in free list112// - free block list splinter threshold113114// 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.116const size_t at_least_allocated = _alloc_count.total_size() - _dealloc_count.total_size();117118// At most we allocated this:119const size_t max_word_overhead_per_alloc = 4;120const size_t at_most_allocated = _alloc_count.total_size() + max_word_overhead_per_alloc * _alloc_count.count();121122ASSERT_LE(at_least_allocated, in_use_stats._used_words - stats._free_blocks_word_size);123ASSERT_GE(at_most_allocated, in_use_stats._used_words - stats._free_blocks_word_size);124125}126127public:128129MetaspaceArena* arena() { return _arena; }130131MetaspaceArenaTestBed(ChunkManager* cm, const ArenaGrowthPolicy* alloc_sequence,132SizeAtomicCounter* used_words_counter, SizeRange allocation_range) :133_arena(NULL),134_lock(NULL),135_allocation_range(allocation_range),136_size_of_last_failed_allocation(0),137_allocations(NULL),138_alloc_count(),139_dealloc_count()140{141_lock = new Mutex(Monitor::native, "gtest-MetaspaceArenaTestBed-lock", false, Monitor::_safepoint_check_never);142// Lock during space creation, since this is what happens in the VM too143// (see ClassLoaderData::metaspace_non_null(), which we mimick here).144MutexLocker ml(_lock, Mutex::_no_safepoint_check_flag);145_arena = new MetaspaceArena(cm, alloc_sequence, _lock, used_words_counter, "gtest-MetaspaceArenaTestBed-sm");146}147148~MetaspaceArenaTestBed() {149150verify_arena_statistics();151152allocation_t* a = _allocations;153while (a != NULL) {154allocation_t* b = a->next;155a->verify();156FREE_C_HEAP_OBJ(a);157a = b;158}159160DEBUG_ONLY(_arena->verify();)161162// Delete MetaspaceArena. That should clean up all metaspace.163delete _arena;164delete _lock;165166}167168size_t words_allocated() const { return _alloc_count.total_size(); }169int num_allocations() const { return _alloc_count.count(); }170171size_t size_of_last_failed_allocation() const { return _size_of_last_failed_allocation; }172173// Allocate a random amount. Return false if the allocation failed.174bool checked_random_allocate() {175size_t word_size = 1 + _allocation_range.random_value();176MetaWord* p = _arena->allocate(word_size);177if (p != NULL) {178EXPECT_TRUE(is_aligned(p, sizeof(MetaWord)));179allocation_t* a = NEW_C_HEAP_OBJ(allocation_t, mtInternal);180a->word_size = word_size;181a->p = p;182a->mark();183a->next = _allocations;184_allocations = a;185_alloc_count.add(word_size);186if ((_alloc_count.count() % 20) == 0) {187verify_arena_statistics();188DEBUG_ONLY(_arena->verify();)189}190return true;191} else {192_size_of_last_failed_allocation = word_size;193}194return false;195}196197// Deallocate a random allocation198void checked_random_deallocate() {199allocation_t* a = _allocations;200while (a && a->p != NULL && os::random() % 10 != 0) {201a = a->next;202}203if (a != NULL && a->p != NULL) {204a->verify();205_arena->deallocate(a->p, a->word_size);206_dealloc_count.add(a->word_size);207a->p = NULL; a->word_size = 0;208if ((_dealloc_count.count() % 20) == 0) {209verify_arena_statistics();210DEBUG_ONLY(_arena->verify();)211}212}213}214215}; // End: MetaspaceArenaTestBed216217class MetaspaceArenaTest {218219MetaspaceGtestContext _context;220221SizeAtomicCounter _used_words_counter;222223SparseArray<MetaspaceArenaTestBed*> _testbeds;224IntCounter _num_beds;225226//////// Bed creation, destruction ///////227228void create_new_test_bed_at(int slotindex, const ArenaGrowthPolicy* growth_policy, SizeRange allocation_range) {229DEBUG_ONLY(_testbeds.check_slot_is_null(slotindex));230MetaspaceArenaTestBed* bed = new MetaspaceArenaTestBed(&_context.cm(), growth_policy,231&_used_words_counter, allocation_range);232_testbeds.set_at(slotindex, bed);233_num_beds.increment();234}235236void create_random_test_bed_at(int slotindex) {237SizeRange allocation_range(1, 100); // randomize too?238const ArenaGrowthPolicy* growth_policy = ArenaGrowthPolicy::policy_for_space_type(239(fifty_fifty() ? Metaspace::StandardMetaspaceType : Metaspace::ReflectionMetaspaceType),240fifty_fifty());241create_new_test_bed_at(slotindex, growth_policy, allocation_range);242}243244// Randomly create a random test bed at a random slot, and return its slot index245// (returns false if we reached max number of test beds)246bool create_random_test_bed() {247const int slot = _testbeds.random_null_slot_index();248if (slot != -1) {249create_random_test_bed_at(slot);250}251return slot;252}253254// Create test beds for all slots255void create_all_test_beds() {256for (int slot = 0; slot < _testbeds.size(); slot++) {257if (_testbeds.slot_is_null(slot)) {258create_random_test_bed_at(slot);259}260}261}262263void delete_test_bed_at(int slotindex) {264DEBUG_ONLY(_testbeds.check_slot_is_not_null(slotindex));265MetaspaceArenaTestBed* bed = _testbeds.at(slotindex);266delete bed; // This will return all its memory to the chunk manager267_testbeds.set_at(slotindex, NULL);268_num_beds.decrement();269}270271// Randomly delete a random test bed at a random slot272// Return false if there are no test beds to delete.273bool delete_random_test_bed() {274const int slotindex = _testbeds.random_non_null_slot_index();275if (slotindex != -1) {276delete_test_bed_at(slotindex);277return true;278}279return false;280}281282// Delete all test beds.283void delete_all_test_beds() {284for (int slot = _testbeds.first_non_null_slot(); slot != -1; slot = _testbeds.next_non_null_slot(slot)) {285delete_test_bed_at(slot);286}287}288289//////// Allocating metaspace from test beds ///////290291bool random_allocate_from_testbed(int slotindex) {292DEBUG_ONLY(_testbeds.check_slot_is_not_null(slotindex);)293MetaspaceArenaTestBed* bed = _testbeds.at(slotindex);294bool success = bed->checked_random_allocate();295if (success == false) {296// We must have hit a limit.297EXPECT_LT(_context.commit_limiter().possible_expansion_words(),298metaspace::get_raw_word_size_for_requested_word_size(bed->size_of_last_failed_allocation()));299}300return success;301}302303// Allocate multiple times random sizes from a single MetaspaceArena.304bool random_allocate_multiple_times_from_testbed(int slotindex, int num_allocations) {305bool success = true;306int n = 0;307while (success && n < num_allocations) {308success = random_allocate_from_testbed(slotindex);309n++;310}311return success;312}313314// Allocate multiple times random sizes from a single random MetaspaceArena.315bool random_allocate_random_times_from_random_testbed() {316int slot = _testbeds.random_non_null_slot_index();317bool success = false;318if (slot != -1) {319const int n = IntRange(5, 20).random_value();320success = random_allocate_multiple_times_from_testbed(slot, n);321}322return success;323}324325/////// Deallocating from testbed ///////////////////326327void deallocate_from_testbed(int slotindex) {328DEBUG_ONLY(_testbeds.check_slot_is_not_null(slotindex);)329MetaspaceArenaTestBed* bed = _testbeds.at(slotindex);330bed->checked_random_deallocate();331}332333void deallocate_from_random_testbed() {334int slot = _testbeds.random_non_null_slot_index();335if (slot != -1) {336deallocate_from_testbed(slot);337}338}339340/////// Stats ///////////////////////////////////////341342int get_total_number_of_allocations() const {343int sum = 0;344for (int i = _testbeds.first_non_null_slot(); i != -1; i = _testbeds.next_non_null_slot(i)) {345sum += _testbeds.at(i)->num_allocations();346}347return sum;348}349350size_t get_total_words_allocated() const {351size_t sum = 0;352for (int i = _testbeds.first_non_null_slot(); i != -1; i = _testbeds.next_non_null_slot(i)) {353sum += _testbeds.at(i)->words_allocated();354}355return sum;356}357358public:359360MetaspaceArenaTest(size_t commit_limit, int num_testbeds)361: _context(commit_limit),362_testbeds(num_testbeds),363_num_beds()364{}365366~MetaspaceArenaTest () {367368delete_all_test_beds();369370}371372//////////////// Tests ////////////////////////373374void test() {375376// In a big loop, randomly chose one of these actions377// - creating a test bed (simulates a new loader creation)378// - allocating from a test bed (simulates allocating metaspace for a loader)379// - (rarely) deallocate (simulates metaspace deallocation, e.g. class redefinitions)380// - delete a test bed (simulates collection of a loader and subsequent return of metaspace to freelists)381382const int iterations = 10000;383384// Lets have a ceiling on number of words allocated (this is independent from the commit limit)385const size_t max_allocation_size = 8 * M;386387bool force_bed_deletion = false;388389for (int niter = 0; niter < iterations; niter++) {390391const int r = IntRange(100).random_value();392393if (force_bed_deletion || r < 10) {394395force_bed_deletion = false;396delete_random_test_bed();397398} else if (r < 20 || _num_beds.get() < (unsigned)_testbeds.size() / 2) {399400create_random_test_bed();401402} else if (r < 95) {403404// If allocation fails, we hit the commit limit and should delete some beds first405force_bed_deletion = ! random_allocate_random_times_from_random_testbed();406407} else {408409// Note: does not affect the used words counter.410deallocate_from_random_testbed();411412}413414// If we are close to our quota, start bed deletion415if (_used_words_counter.get() >= max_allocation_size) {416417force_bed_deletion = true;418419}420421}422423}424425};426427// 32 parallel MetaspaceArena objects, random allocating without commit limit428TEST_VM(metaspace, MetaspaceArena_random_allocs_32_beds_no_commit_limit) {429MetaspaceArenaTest test(max_uintx, 32);430test.test();431}432433// 32 parallel Metaspace arena objects, random allocating with commit limit434TEST_VM(metaspace, MetaspaceArena_random_allocs_32_beds_with_commit_limit) {435MetaspaceArenaTest test(2 * M, 32);436test.test();437}438439// A single MetaspaceArena, random allocating without commit limit. This should exercise440// chunk enlargement since allocation is undisturbed.441TEST_VM(metaspace, MetaspaceArena_random_allocs_1_bed_no_commit_limit) {442MetaspaceArenaTest test(max_uintx, 1);443test.test();444}445446447448