Path: blob/master/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp
41149 views
/*1* Copyright (c) 2006, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*22*/2324#ifndef SHARE_GC_PARALLEL_MUTABLENUMASPACE_HPP25#define SHARE_GC_PARALLEL_MUTABLENUMASPACE_HPP2627#include "gc/parallel/mutableSpace.hpp"28#include "gc/shared/gcUtil.hpp"29#include "runtime/globals.hpp"30#include "utilities/growableArray.hpp"31#include "utilities/macros.hpp"3233/*34* The NUMA-aware allocator (MutableNUMASpace) is basically a modification35* of MutableSpace which preserves interfaces but implements different36* functionality. The space is split into chunks for each locality group37* (resizing for adaptive size policy is also supported). For each thread38* allocations are performed in the chunk corresponding to the home locality39* group of the thread. Whenever any chunk fills-in the young generation40* collection occurs.41* The chunks can be also be adaptively resized. The idea behind the adaptive42* sizing is to reduce the loss of the space in the eden due to fragmentation.43* The main cause of fragmentation is uneven allocation rates of threads.44* The allocation rate difference between locality groups may be caused either by45* application specifics or by uneven LWP distribution by the OS. Besides,46* application can have less threads then the number of locality groups.47* In order to resize the chunk we measure the allocation rate of the48* application between collections. After that we reshape the chunks to reflect49* the allocation rate pattern. The AdaptiveWeightedAverage exponentially50* decaying average is used to smooth the measurements. The NUMASpaceResizeRate51* parameter is used to control the adaptation speed by restricting the number of52* bytes that can be moved during the adaptation phase.53* Chunks may contain pages from a wrong locality group. The page-scanner has54* been introduced to address the problem. Remote pages typically appear due to55* the memory shortage in the target locality group. Besides Solaris would56* allocate a large page from the remote locality group even if there are small57* local pages available. The page-scanner scans the pages right after the58* collection and frees remote pages in hope that subsequent reallocation would59* be more successful. This approach proved to be useful on systems with high60* load where multiple processes are competing for the memory.61*/6263class MutableNUMASpace : public MutableSpace {64friend class VMStructs;6566class LGRPSpace : public CHeapObj<mtGC> {67int _lgrp_id;68MutableSpace* _space;69MemRegion _invalid_region;70AdaptiveWeightedAverage *_alloc_rate;71bool _allocation_failed;7273struct SpaceStats {74size_t _local_space, _remote_space, _unbiased_space, _uncommited_space;75size_t _large_pages, _small_pages;7677SpaceStats() {78_local_space = 0;79_remote_space = 0;80_unbiased_space = 0;81_uncommited_space = 0;82_large_pages = 0;83_small_pages = 0;84}85};8687SpaceStats _space_stats;8889char* _last_page_scanned;90char* last_page_scanned() { return _last_page_scanned; }91void set_last_page_scanned(char* p) { _last_page_scanned = p; }92public:93LGRPSpace(int l, size_t alignment) : _lgrp_id(l), _allocation_failed(false), _last_page_scanned(NULL) {94_space = new MutableSpace(alignment);95_alloc_rate = new AdaptiveWeightedAverage(NUMAChunkResizeWeight);96}97~LGRPSpace() {98delete _space;99delete _alloc_rate;100}101102void add_invalid_region(MemRegion r) {103if (!_invalid_region.is_empty()) {104_invalid_region.set_start(MIN2(_invalid_region.start(), r.start()));105_invalid_region.set_end(MAX2(_invalid_region.end(), r.end()));106} else {107_invalid_region = r;108}109}110111static bool equals(void* lgrp_id_value, LGRPSpace* p) {112return *(int*)lgrp_id_value == p->lgrp_id();113}114115// Report a failed allocation.116void set_allocation_failed() { _allocation_failed = true; }117118void sample() {119// If there was a failed allocation make allocation rate equal120// to the size of the whole chunk. This ensures the progress of121// the adaptation process.122size_t alloc_rate_sample;123if (_allocation_failed) {124alloc_rate_sample = space()->capacity_in_bytes();125_allocation_failed = false;126} else {127alloc_rate_sample = space()->used_in_bytes();128}129alloc_rate()->sample(alloc_rate_sample);130}131132MemRegion invalid_region() const { return _invalid_region; }133void set_invalid_region(MemRegion r) { _invalid_region = r; }134int lgrp_id() const { return _lgrp_id; }135MutableSpace* space() const { return _space; }136AdaptiveWeightedAverage* alloc_rate() const { return _alloc_rate; }137void clear_alloc_rate() { _alloc_rate->clear(); }138SpaceStats* space_stats() { return &_space_stats; }139void clear_space_stats() { _space_stats = SpaceStats(); }140141void accumulate_statistics(size_t page_size);142void scan_pages(size_t page_size, size_t page_count);143};144145GrowableArray<LGRPSpace*>* _lgrp_spaces;146size_t _page_size;147unsigned _adaptation_cycles, _samples_count;148149bool _must_use_large_pages;150151void set_page_size(size_t psz) { _page_size = psz; }152size_t page_size() const { return _page_size; }153154unsigned adaptation_cycles() { return _adaptation_cycles; }155void set_adaptation_cycles(int v) { _adaptation_cycles = v; }156157unsigned samples_count() { return _samples_count; }158void increment_samples_count() { ++_samples_count; }159160size_t _base_space_size;161void set_base_space_size(size_t v) { _base_space_size = v; }162size_t base_space_size() const { return _base_space_size; }163164// Check if the NUMA topology has changed. Add and remove spaces if needed.165// The update can be forced by setting the force parameter equal to true.166bool update_layout(bool force);167// Bias region towards the lgrp.168void bias_region(MemRegion mr, int lgrp_id);169// Free pages in a given region.170void free_region(MemRegion mr);171// Get current chunk size.172size_t current_chunk_size(int i);173// Get default chunk size (equally divide the space).174size_t default_chunk_size();175// Adapt the chunk size to follow the allocation rate.176size_t adaptive_chunk_size(int i, size_t limit);177// Scan and free invalid pages.178void scan_pages(size_t page_count);179// Return the bottom_region and the top_region. Align them to page_size() boundary.180// |------------------new_region---------------------------------|181// |----bottom_region--|---intersection---|------top_region------|182void select_tails(MemRegion new_region, MemRegion intersection,183MemRegion* bottom_region, MemRegion *top_region);184// Try to merge the invalid region with the bottom or top region by decreasing185// the intersection area. Return the invalid_region aligned to the page_size()186// boundary if it's inside the intersection. Return non-empty invalid_region187// if it lies inside the intersection (also page-aligned).188// |------------------new_region---------------------------------|189// |----------------|-------invalid---|--------------------------|190// |----bottom_region--|---intersection---|------top_region------|191void merge_regions(MemRegion new_region, MemRegion* intersection,192MemRegion *invalid_region);193194public:195GrowableArray<LGRPSpace*>* lgrp_spaces() const { return _lgrp_spaces; }196MutableNUMASpace(size_t alignment);197virtual ~MutableNUMASpace();198// Space initialization.199virtual void initialize(MemRegion mr,200bool clear_space,201bool mangle_space,202bool setup_pages = SetupPages,203WorkGang* pretouch_gang = NULL);204// Update space layout if necessary. Do all adaptive resizing job.205virtual void update();206// Update allocation rate averages.207virtual void accumulate_statistics();208209virtual void clear(bool mangle_space);210virtual void mangle_unused_area() PRODUCT_RETURN;211virtual void mangle_unused_area_complete() PRODUCT_RETURN;212virtual void mangle_region(MemRegion mr) PRODUCT_RETURN;213virtual void check_mangled_unused_area(HeapWord* limit) PRODUCT_RETURN;214virtual void check_mangled_unused_area_complete() PRODUCT_RETURN;215virtual void set_top_for_allocations(HeapWord* v) PRODUCT_RETURN;216virtual void set_top_for_allocations() PRODUCT_RETURN;217218virtual void ensure_parsability();219virtual size_t used_in_words() const;220virtual size_t free_in_words() const;221222using MutableSpace::capacity_in_words;223virtual size_t capacity_in_words(Thread* thr) const;224virtual size_t tlab_capacity(Thread* thr) const;225virtual size_t tlab_used(Thread* thr) const;226virtual size_t unsafe_max_tlab_alloc(Thread* thr) const;227228// Allocation (return NULL if full)229virtual HeapWord* cas_allocate(size_t word_size);230231// Debugging232virtual void print_on(outputStream* st) const;233virtual void print_short_on(outputStream* st) const;234virtual void verify();235236virtual void set_top(HeapWord* value);237};238239#endif // SHARE_GC_PARALLEL_MUTABLENUMASPACE_HPP240241242