--- old/src/hotspot/share/memory/metaspace/metaspaceArena.cpp 2020-09-04 13:58:28.545613379 +0200 +++ /dev/null 2020-09-04 12:37:41.765504620 +0200 @@ -1,556 +0,0 @@ -/* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2020 SAP SE. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#include "precompiled.hpp" - -#include "logging/log.hpp" -#include "logging/logStream.hpp" -#include "memory/metaspace/allocationGuard.hpp" -#include "memory/metaspace/arenaGrowthPolicy.hpp" -#include "memory/metaspace/freeBlocks.hpp" -#include "memory/metaspace/chunkManager.hpp" -#include "memory/metaspace/internStat.hpp" -#include "memory/metaspace/metachunk.hpp" -#include "memory/metaspace/metaspaceArena.hpp" -#include "memory/metaspace/metaspaceCommon.hpp" -#include "memory/metaspace/metaspaceStatistics.hpp" -#include "memory/metaspace/virtualSpaceList.hpp" -#include "runtime/atomic.hpp" -#include "runtime/init.hpp" -#include "services/memoryService.hpp" -#include "utilities/align.hpp" -#include "utilities/debug.hpp" -#include "utilities/globalDefinitions.hpp" - -namespace metaspace { - -#define LOGFMT "Arena @" PTR_FORMAT " (%s)" -#define LOGFMT_ARGS p2i(this), this->_name - -// Given a net allocation word size, return the raw word size we actually allocate. -// Note: externally visible for gtests. -//static -size_t get_raw_allocation_word_size(size_t net_word_size) { - - size_t byte_size = net_word_size * BytesPerWord; - - // Deallocated metablocks are kept in a binlist which limits their minimal - // size to at least the size of a binlist item (2 words). - byte_size = MAX2(byte_size, FreeBlocks::minimal_word_size * BytesPerWord); - - // Metaspace allocations are aligned to word size. - byte_size = align_up(byte_size, allocation_alignment_bytes); - - // If we guard allocations, we need additional space for a prefix. -#ifdef ASSERT - if (Settings::use_allocation_guard()) { - byte_size += align_up(prefix_size(), allocation_alignment_bytes); - } -#endif - - size_t word_size = byte_size / BytesPerWord; - - assert(word_size * BytesPerWord == byte_size, "Sanity"); - - return word_size; - -} - -// Returns the level of the next chunk to be added, acc to growth policy. -chunklevel_t MetaspaceArena::next_chunk_level() const { - const int growth_step = _chunks.count(); - return _growth_policy->get_level_at_step(growth_step); -} - -// Given a chunk, add its remaining free committed space to the free block list. -void MetaspaceArena::salvage_chunk(Metachunk* c) { - - if (Settings::handle_deallocations() == false) { - return; - } - - assert_lock_strong(lock()); - - // If the chunk is completely empty, just return it to the chunk manager. - if (c->used_words() == 0) { - UL2(trace, "salvage: returning empty chunk " METACHUNK_FORMAT ".", METACHUNK_FORMAT_ARGS(c)); - _chunk_manager->return_chunk(c); - return; - } - - size_t remaining_words = c->free_below_committed_words(); - - if (remaining_words > FreeBlocks::minimal_word_size) { - - UL2(trace, "salvaging chunk " METACHUNK_FULL_FORMAT ".", METACHUNK_FULL_FORMAT_ARGS(c)); - - MetaWord* ptr = c->allocate(remaining_words); - assert(ptr != NULL, "Should have worked"); - _total_used_words_counter->increment_by(remaining_words); - - add_allocation_to_fbl(ptr, remaining_words); - - // After this operation: the chunk should have no free committed space left. - assert(c->free_below_committed_words() == 0, - "Salvaging chunk failed (chunk " METACHUNK_FULL_FORMAT ").", - METACHUNK_FULL_FORMAT_ARGS(c)); - - } - -} - -// Allocate a new chunk from the underlying chunk manager able to hold at least -// requested word size. -Metachunk* MetaspaceArena::allocate_new_chunk(size_t requested_word_size) { - - assert_lock_strong(lock()); - - // Should this ever happen, we need to increase the maximum possible chunk size. - guarantee(requested_word_size <= chunklevel::MAX_CHUNK_WORD_SIZE, - "Requested size too large (" SIZE_FORMAT ") - max allowed size per allocation is " SIZE_FORMAT ".", - requested_word_size, chunklevel::MAX_CHUNK_WORD_SIZE); - - const int growth_step = _chunks.count(); - const chunklevel_t max_level = chunklevel::level_fitting_word_size(requested_word_size); - const chunklevel_t preferred_level = MIN2(max_level, next_chunk_level()); - - Metachunk* c = _chunk_manager->get_chunk(preferred_level, max_level, requested_word_size); - if (c == NULL) { - return NULL; - } - - assert(c->is_in_use(), "Wrong chunk state."); - assert(c->free_below_committed_words() >= requested_word_size, "Chunk not committed"); - - return c; - -} - -void MetaspaceArena::add_allocation_to_fbl(MetaWord* p, size_t word_size) { - assert(Settings::handle_deallocations(), "Sanity"); - if (_fbl == NULL) { - _fbl = new FreeBlocks(); // Create only on demand - } - _fbl->add_block(p, word_size); -} - -MetaspaceArena::MetaspaceArena(ChunkManager* chunk_manager, - const ArenaGrowthPolicy* growth_policy, - Mutex* lock, - SizeAtomicCounter* total_used_words_counter, - const char* name) -: _lock(lock), - _chunk_manager(chunk_manager), - _growth_policy(growth_policy), - _chunks(), - _fbl(NULL), - _total_used_words_counter(total_used_words_counter), - _name(name) -{ - UL(debug, ": born."); - - // Update statistics - InternalStats::inc_num_arena_births(); -} - -MetaspaceArena::~MetaspaceArena() { - - DEBUG_ONLY(verify(true);) - - MutexLocker fcl(lock(), Mutex::_no_safepoint_check_flag); - - MemRangeCounter return_counter; - - Metachunk* c = _chunks.first(); - Metachunk* c2 = NULL; - - while(c) { - c2 = c->next(); - return_counter.add(c->used_words()); - DEBUG_ONLY(c->set_prev(NULL);) - DEBUG_ONLY(c->set_next(NULL);) - UL2(debug, "return chunk: " METACHUNK_FORMAT ".", METACHUNK_FORMAT_ARGS(c)); - _chunk_manager->return_chunk(c); - // c may be invalid after return_chunk(c) was called. Don't access anymore. - c = c2; - } - - UL2(info, "returned %d chunks, total capacity " SIZE_FORMAT " words.", - return_counter.count(), return_counter.total_size()); - - _total_used_words_counter->decrement_by(return_counter.total_size()); - - DEBUG_ONLY(chunk_manager()->verify(true);) - - delete _fbl; - - UL(debug, ": dies."); - - // Update statistics - InternalStats::inc_num_arena_deaths(); - -} - -// Attempt to enlarge the current chunk to make it large enough to hold at least -// requested_word_size additional words. -// -// On success, true is returned, false otherwise. -bool MetaspaceArena::attempt_enlarge_current_chunk(size_t requested_word_size) { - - assert_lock_strong(lock()); - - Metachunk* c = current_chunk(); - assert(c->free_words() < requested_word_size, "Sanity"); - - // Not if chunk enlargment is switched off... - if (Settings::enlarge_chunks_in_place() == false) { - return false; - } - - // ... we also disallow it for very large chunks... - if (c->word_size() > Settings::enlarge_chunks_in_place_max_word_size()) { - return false; - } - - // ... nor if we are already a root chunk ... - if (c->is_root_chunk()) { - return false; - } - - // ... nor if the combined size of chunk content and new content would bring us above the size of a root chunk ... - if ((c->used_words() + requested_word_size) > metaspace::chunklevel::MAX_CHUNK_WORD_SIZE) { - return false; - } - - const chunklevel_t new_level = - chunklevel::level_fitting_word_size(c->used_words() + requested_word_size); - assert(new_level < c->level(), "Sanity"); - - // Atm we only enlarge by one level (so, doubling the chunk in size). So, if the requested enlargement - // would require the chunk to more than double in size, we bail. But this covers about 99% of all cases, - // so this is good enough. - if (new_level < c->level() - 1) { - return false; - } - - // This only works if chunk is the leader of its buddy pair (and also if buddy - // is free and unsplit, but that we cannot check outside of metaspace lock). - if (!c->is_leader()) { - return false; - } - - // If the size added to the chunk would be larger than allowed for the next growth step - // dont enlarge. - if (next_chunk_level() > c->level()) { - return false; - } - - bool success = _chunk_manager->attempt_enlarge_chunk(c); - - assert(success == false || c->free_words() >= requested_word_size, "Sanity"); - - return success; - -} - -// Allocate memory from Metaspace. -// 1) Attempt to allocate from the free block list. -// 2) Attempt to allocate from the current chunk. -// 3) Attempt to enlarge the current chunk in place if it is too small. -// 4) Attempt to get a new chunk and allocate from that chunk. -// At any point, if we hit a commit limit, we return NULL. -MetaWord* MetaspaceArena::allocate(size_t requested_word_size) { - - MutexLocker cl(lock(), Mutex::_no_safepoint_check_flag); - - UL2(trace, "requested " SIZE_FORMAT " words.", requested_word_size); - - MetaWord* p = NULL; - - const size_t raw_word_size = get_raw_word_size_for_requested_word_size(requested_word_size); - - // 1) Attempt to allocate from the free blocks list - if (Settings::handle_deallocations() && _fbl != NULL && !_fbl->is_empty()) { - p = _fbl->get_block(raw_word_size); - if (p != NULL) { - DEBUG_ONLY(InternalStats::inc_num_allocs_from_deallocated_blocks();) - UL2(trace, "taken from fbl (now: %d, " SIZE_FORMAT ").", - _fbl->count(), _fbl->total_size()); - // Note: Space in the freeblock dictionary counts as already used (see retire_current_chunk()) - - // that means that we do not modify any counters and therefore can skip the epilog. - return p; - } - } - - bool current_chunk_too_small = false; - bool commit_failure = false; - - if (current_chunk() != NULL) { - - // 2) Attempt to satisfy the allocation from the current chunk. - - // If the current chunk is too small to hold the requested size, attempt to enlarge it. - // If that fails, retire the chunk. - if (current_chunk()->free_words() < raw_word_size) { - if (!attempt_enlarge_current_chunk(raw_word_size)) { - current_chunk_too_small = true; - } else { - DEBUG_ONLY(InternalStats::inc_num_chunks_enlarged();) - UL(debug, "enlarged chunk."); - } - } - - // Commit the chunk far enough to hold the requested word size. If that fails, we - // hit a limit (either GC threshold or MaxMetaspaceSize). In that case retire the - // chunk. - if (!current_chunk_too_small) { - if (!current_chunk()->ensure_committed_additional(raw_word_size)) { - UL2(info, "commit failure (requested size: " SIZE_FORMAT ")", raw_word_size); - commit_failure = true; - } - } - - // Allocate from the current chunk. This should work now. - if (!current_chunk_too_small && !commit_failure) { - p = current_chunk()->allocate(raw_word_size); - assert(p != NULL, "Allocation from chunk failed."); - } - - } - - if (p == NULL) { - - // If we are here, we either had no current chunk to begin with or it was deemed insufficient. - assert(current_chunk() == NULL || - current_chunk_too_small || commit_failure, "Sanity"); - - Metachunk* new_chunk = allocate_new_chunk(raw_word_size); - - if (new_chunk != NULL) { - - UL2(debug, "allocated new chunk " METACHUNK_FORMAT " for requested word size " SIZE_FORMAT ".", - METACHUNK_FORMAT_ARGS(new_chunk), requested_word_size); - - assert(new_chunk->free_below_committed_words() >= raw_word_size, "Sanity"); - - // We have a new chunk. Before making it the current chunk, retire the old one. - if (current_chunk() != NULL) { - salvage_chunk(current_chunk()); - DEBUG_ONLY(InternalStats::inc_num_chunks_retired();) - } - - _chunks.add(new_chunk); - - // Now, allocate from that chunk. That should work. - p = current_chunk()->allocate(raw_word_size); - assert(p != NULL, "Allocation from chunk failed."); - - } else { - UL2(info, "failed to allocate new chunk for requested word size " SIZE_FORMAT ".", requested_word_size); - } - - } - -#ifdef ASSERT - // When using allocation guards, establish a prefix. - if (p != NULL && Settings::use_allocation_guard()) { - p = establish_prefix(p, raw_word_size); - } -#endif - - if (p == NULL) { - InternalStats::inc_num_allocs_failed_limit(); - } else { - DEBUG_ONLY(InternalStats::inc_num_allocs();) - _total_used_words_counter->increment_by(raw_word_size); - } - - SOMETIMES(verify_locked(true);) - - if (p == NULL) { - UL(info, "allocation failed, returned NULL."); - } else { - UL2(trace, "returned " PTR_FORMAT ".", p2i(p)); - } - - return p; - -} - -// Prematurely returns a metaspace allocation to the _block_freelists -// because it is not needed anymore (requires CLD lock to be active). -void MetaspaceArena::deallocate_locked(MetaWord* p, size_t word_size) { - - if (Settings::handle_deallocations() == false) { - return; - } - - assert_lock_strong(lock()); - - // At this point a current chunk must exist since we only deallocate if we did allocate before. - assert(current_chunk() != NULL, "stray deallocation?"); - - assert(is_valid_area(p, word_size), - "Pointer range not part of this Arena and cannot be deallocated: (" PTR_FORMAT ".." PTR_FORMAT ").", - p2i(p), p2i(p + word_size)); - - UL2(trace, "deallocating " PTR_FORMAT ", word size: " SIZE_FORMAT ".", - p2i(p), word_size); - - size_t raw_word_size = get_raw_word_size_for_requested_word_size(word_size); - add_allocation_to_fbl(p, raw_word_size); - - DEBUG_ONLY(verify_locked(false);) - -} - -// Prematurely returns a metaspace allocation to the _block_freelists because it is not -// needed anymore. -void MetaspaceArena::deallocate(MetaWord* p, size_t word_size) { - MutexLocker cl(lock(), Mutex::_no_safepoint_check_flag); - deallocate_locked(p, word_size); -} - -// Update statistics. This walks all in-use chunks. -void MetaspaceArena::add_to_statistics(arena_stats_t* out) const { - - MutexLocker cl(lock(), Mutex::_no_safepoint_check_flag); - - for (const Metachunk* c = _chunks.first(); c != NULL; c = c->next()) { - in_use_chunk_stats_t& ucs = out->stats[c->level()]; - ucs.num ++; - ucs.word_size += c->word_size(); - ucs.committed_words += c->committed_words(); - ucs.used_words += c->used_words(); - // Note: for free and waste, we only count what's committed. - if (c == current_chunk()) { - ucs.free_words += c->free_below_committed_words(); - } else { - ucs.waste_words += c->free_below_committed_words(); - } - } - - if (_fbl != NULL) { - out->free_blocks_num += _fbl->count(); - out->free_blocks_word_size += _fbl->total_size(); - } - - SOMETIMES(out->verify();) - -} - -// Convenience method to get the most important usage statistics. -// For deeper analysis use add_to_statistics(). -void MetaspaceArena::usage_numbers(size_t* p_used_words, size_t* p_committed_words, size_t* p_capacity_words) const { - MutexLocker cl(lock(), Mutex::_no_safepoint_check_flag); - size_t used = 0, comm = 0, cap = 0; - for (const Metachunk* c = _chunks.first(); c != NULL; c = c->next()) { - used += c->used_words(); - comm += c->committed_words(); - cap += c->word_size(); - } - if (p_used_words != NULL) { - *p_used_words = used; - } - if (p_committed_words != NULL) { - *p_committed_words = comm; - } - if (p_capacity_words != NULL) { - *p_capacity_words = cap; - } -} - - -#ifdef ASSERT - -void MetaspaceArena::verify_locked(bool slow) const { - - assert_lock_strong(lock()); - - assert(_growth_policy != NULL && _chunk_manager != NULL, "Sanity"); - - _chunks.verify(); - - if (_fbl != NULL) { - _fbl->verify(); - } - - // In slow mode, verify guard zones of all allocations - if (slow && Settings::use_allocation_guard()) { - for (const Metachunk* c = _chunks.first(); c != NULL; c = c->next()) { - const MetaWord* p = c->base(); - while (p < c->top()) { - const prefix_t* pp = (const prefix_t*)p; - check_prefix(pp); - p += pp->word_size; - } - } - } - -} - -void MetaspaceArena::verify(bool slow) const { - - MutexLocker cl(lock(), Mutex::_no_safepoint_check_flag); - verify_locked(slow); - -} - -// Returns true if the area indicated by pointer and size have actually been allocated -// from this arena. -bool MetaspaceArena::is_valid_area(MetaWord* p, size_t word_size) const { - assert(p != NULL && word_size > 0, "Sanity"); - bool found = false; - if (!found) { - for (const Metachunk* c = _chunks.first(); c != NULL && !found; c = c->next()) { - assert(c->is_valid_committed_pointer(p) == - c->is_valid_committed_pointer(p + word_size - 1), "range intersects"); - found = c->is_valid_committed_pointer(p); - } - } - return found; -} - -#endif // ASSERT - -void MetaspaceArena::print_on(outputStream* st) const { - MutexLocker fcl(_lock, Mutex::_no_safepoint_check_flag); - print_on_locked(st); -} - -void MetaspaceArena::print_on_locked(outputStream* st) const { - assert_lock_strong(_lock); - st->print_cr("sm %s: %d chunks, total word size: " SIZE_FORMAT ", committed word size: " SIZE_FORMAT, _name, - _chunks.count(), _chunks.calc_word_size(), _chunks.calc_committed_word_size()); - _chunks.print_on(st); - st->cr(); - st->print_cr("growth-policy " PTR_FORMAT ", lock " PTR_FORMAT ", cm " PTR_FORMAT ", fbl " PTR_FORMAT, - p2i(_growth_policy), p2i(_lock), p2i(_chunk_manager), p2i(_fbl)); -} - - - -} // namespace metaspace -