mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 03:58:21 +00:00
429 lines
18 KiB
C++
429 lines
18 KiB
C++
/*
|
|
* Copyright (c) 2021, 2026, Oracle and/or its affiliates. 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.
|
|
*
|
|
*/
|
|
|
|
#ifndef SHARE_GC_G1_G1CARDSET_HPP
|
|
#define SHARE_GC_G1_G1CARDSET_HPP
|
|
|
|
#include "memory/allocation.hpp"
|
|
#include "memory/memRegion.hpp"
|
|
#include "runtime/atomic.hpp"
|
|
#include "utilities/concurrentHashTable.hpp"
|
|
|
|
class G1CardSetAllocOptions;
|
|
class G1CardSetHashTable;
|
|
class G1CardSetHashTableValue;
|
|
class G1CardSetMemoryManager;
|
|
class Mutex;
|
|
|
|
// The result of an attempt to add a card to that card set.
|
|
enum G1AddCardResult {
|
|
Overflow, // The card set is more than full. The entry may have been added. Need
|
|
// Coarsen and retry.
|
|
Found, // The card is already in the set.
|
|
Added // The card has been added to the set by this attempt.
|
|
};
|
|
|
|
class G1CardSetConfiguration {
|
|
// Holds the number of bits required to cover the maximum card index for the
|
|
// regions covered by this card set.
|
|
uint _inline_ptr_bits_per_card;
|
|
|
|
uint _max_cards_in_array;
|
|
uint _num_buckets_in_howl;
|
|
uint _max_cards_in_card_set;
|
|
uint _cards_in_howl_threshold;
|
|
uint _max_cards_in_howl_bitmap;
|
|
uint _cards_in_howl_bitmap_threshold;
|
|
uint _log2_max_cards_in_howl_bitmap;
|
|
uint _bitmap_hash_mask;
|
|
uint _log2_card_regions_per_heap_region;
|
|
uint _log2_cards_per_card_region;
|
|
|
|
G1CardSetAllocOptions* _card_set_alloc_options;
|
|
|
|
G1CardSetConfiguration(uint inline_ptr_bits_per_card,
|
|
uint max_cards_in_array,
|
|
double cards_in_bitmap_threshold_percent,
|
|
uint num_buckets_in_howl,
|
|
double cards_in_howl_threshold_percent,
|
|
uint max_cards_in_card_set,
|
|
uint log2_card_regions_per_heap_region);
|
|
void init_card_set_alloc_options();
|
|
|
|
void log_configuration();
|
|
public:
|
|
|
|
// Initialize card set configuration from globals.
|
|
G1CardSetConfiguration();
|
|
// Initialize card set configuration from parameters.
|
|
// Testing only.
|
|
G1CardSetConfiguration(uint max_cards_in_array,
|
|
double cards_in_bitmap_threshold_percent,
|
|
uint max_buckets_in_howl,
|
|
double cards_in_howl_threshold_percent,
|
|
uint max_cards_in_cardset,
|
|
uint log2_card_region_per_region);
|
|
|
|
~G1CardSetConfiguration();
|
|
|
|
// Inline pointer configuration
|
|
uint inline_ptr_bits_per_card() const { return _inline_ptr_bits_per_card; }
|
|
uint max_cards_in_inline_ptr() const;
|
|
static uint max_cards_in_inline_ptr(uint bits_per_card);
|
|
|
|
// Array of Cards configuration
|
|
// Maximum number of cards in "Array of Cards" set; 0 to disable.
|
|
// Always coarsen to next level if full, so no specific threshold.
|
|
uint max_cards_in_array() const { return _max_cards_in_array; }
|
|
|
|
// Bitmap within Howl card set container configuration
|
|
uint max_cards_in_howl_bitmap() const { return _max_cards_in_howl_bitmap; }
|
|
// (Approximate) Number of cards in bitmap to coarsen Howl Bitmap to Howl Full.
|
|
uint cards_in_howl_bitmap_threshold() const { return _cards_in_howl_bitmap_threshold; }
|
|
uint log2_max_cards_in_howl_bitmap() const {return _log2_max_cards_in_howl_bitmap;}
|
|
|
|
// Howl card set container configuration
|
|
uint num_buckets_in_howl() const { return _num_buckets_in_howl; }
|
|
// Threshold at which to turn howling arrays into Full.
|
|
uint cards_in_howl_threshold() const { return _cards_in_howl_threshold; }
|
|
uint howl_bitmap_offset(uint card_idx) const { return card_idx & _bitmap_hash_mask; }
|
|
// Given a card index, return the bucket in the array of card sets.
|
|
uint howl_bucket_index(uint card_idx) { return card_idx >> _log2_max_cards_in_howl_bitmap; }
|
|
|
|
// Full card configuration
|
|
// Maximum number of cards in a non-full card set for a single card region. Card sets
|
|
// with more entries per region are coarsened to Full.
|
|
uint max_cards_in_region() const { return _max_cards_in_card_set; }
|
|
|
|
// Heap region virtualization: there are some limitations to how many cards the
|
|
// containers can cover to save memory for the common case. Heap region virtualization
|
|
// allows to use multiple entries in the G1CardSet hash table per area covered
|
|
// by the remembered set (e.g. heap region); each such entry is called "card_region".
|
|
//
|
|
// The next two members give information about how many card regions are there
|
|
// per area (heap region) and how many cards each card region has.
|
|
|
|
// The log2 of the number of card regions per heap region configured.
|
|
uint log2_card_regions_per_heap_region() const { return _log2_card_regions_per_heap_region; }
|
|
// The log2 of the number of cards per card region. This is calculated from max_cards_in_region()
|
|
// and above.
|
|
uint log2_cards_per_card_region() const { return _log2_cards_per_card_region; }
|
|
|
|
// Memory object types configuration
|
|
// Number of distinctly sized memory objects on the card set heap.
|
|
// Currently contains CHT-Nodes, ArrayOfCards, BitMaps, Howl
|
|
static constexpr uint num_mem_object_types() { return 4; }
|
|
// Returns the memory allocation options for the memory objects on the card set heap.
|
|
const G1CardSetAllocOptions* mem_object_alloc_options(uint idx);
|
|
|
|
// For a given memory object, get a descriptive name.
|
|
static const char* mem_object_type_name_str(uint index);
|
|
};
|
|
|
|
// Collects coarsening statistics: how many attempts of each kind and how many
|
|
// failed due to a competing thread doing the coarsening first.
|
|
class G1CardSetCoarsenStats {
|
|
public:
|
|
// Number of entries in the statistics tables: since we index with the source
|
|
// container of the coarsening, this is the total number of combinations of
|
|
// card set containers - 1.
|
|
static constexpr size_t NumCoarsenCategories = 7;
|
|
// Coarsening statistics for the possible ContainerPtr in the Howl card set
|
|
// start from this offset.
|
|
static constexpr size_t CoarsenHowlOffset = 4;
|
|
|
|
private:
|
|
// Indices are "from" indices.
|
|
Atomic<size_t> _coarsen_from[NumCoarsenCategories];
|
|
Atomic<size_t> _coarsen_collision[NumCoarsenCategories];
|
|
|
|
public:
|
|
G1CardSetCoarsenStats() { reset(); }
|
|
|
|
void reset();
|
|
|
|
void set(G1CardSetCoarsenStats& other);
|
|
|
|
void subtract_from(G1CardSetCoarsenStats& other);
|
|
|
|
// Record a coarsening for the given tag/category. Collision should be true if
|
|
// this coarsening lost the race to do the coarsening of that category.
|
|
void record_coarsening(uint tag, bool collision);
|
|
|
|
void print_on(outputStream* out);
|
|
};
|
|
|
|
// Set of card indexes comprising a remembered set on the Java heap. Card
|
|
// size is assumed to be card table card size.
|
|
//
|
|
// Technically it is implemented using a ConcurrentHashTable that stores a card
|
|
// set container for every region containing at least one card.
|
|
//
|
|
// There are in total five different containers, encoded in the ConcurrentHashTable
|
|
// node as ContainerPtr. A ContainerPtr may cover the whole region or just a part of
|
|
// it.
|
|
// See its description below for more information.
|
|
class G1CardSet : public CHeapObj<mtGCCardSet> {
|
|
friend class G1CardSetTest;
|
|
friend class G1CardSetMtTestTask;
|
|
friend class G1CheckCardClosure;
|
|
|
|
friend class G1TransferCard;
|
|
|
|
friend class G1ReleaseCardsets;
|
|
|
|
// When splitting addresses into region and card within that region, the logical
|
|
// shift value to get the region.
|
|
static uint _split_card_shift;
|
|
// When splitting addresses into region and card within that region, the mask
|
|
// to get the offset within the region.
|
|
static size_t _split_card_mask;
|
|
|
|
static G1CardSetCoarsenStats _coarsen_stats; // Coarsening statistics since VM start.
|
|
static G1CardSetCoarsenStats _last_coarsen_stats; // Coarsening statistics before last GC.
|
|
public:
|
|
// Two lower bits are used to encode the card set container types
|
|
static const uintptr_t ContainerPtrHeaderSize = 2;
|
|
|
|
// ContainerPtr represents the card set container type of a given covered area.
|
|
// It encodes a type in the LSBs, in addition to having a few significant values.
|
|
//
|
|
// Possible encodings:
|
|
//
|
|
// 0...00000 free (Empty, should never happen on a top-level ContainerPtr)
|
|
// 1...11111 full All card indexes in the whole area this ContainerPtr covers are part of this container.
|
|
// X...XXX00 inline-ptr-cards A handful of card indexes covered by this ContainerPtr are encoded within the ContainerPtr.
|
|
// X...XXX01 array of cards The container is a contiguous array of card indexes.
|
|
// X...XXX10 bitmap The container uses a bitmap to determine whether a given index is part of this set.
|
|
// X...XXX11 howl This is a card set container containing an array of ContainerPtr, with each ContainerPtr
|
|
// limited to a sub-range of the original range. Currently only one level of this
|
|
// container is supported.
|
|
//
|
|
// The container's pointer starts off with an inline container and is then subsequently
|
|
// coarsened as more cards are added.
|
|
//
|
|
// Coarsening happens in the order below:
|
|
// ContainerInlinePtr -> ContainerArrayOfCards -> ContainerHowl -> Full
|
|
//
|
|
// There is intentionally no bitmap based container that covers a full region; first,
|
|
// a whole region is covered very well (and more flexibly) using the howl container and
|
|
// even then the overhead of the ContainerPtr array with all-bitmaps vs. a single bitmap
|
|
// is negligible, and most importantly transferring such a Howl container to a
|
|
// "Full Region Bitmap" is fairly hard without missing entries that are added by
|
|
// concurrent threads.
|
|
//
|
|
// Howl containers are basically arrays of containers. An entry starts off with
|
|
// Free. Further corsening of containers inside the ContainerHowl happens in the order:
|
|
//
|
|
// Free -> ContainerInlinePtr -> ContainerArrayOfCards -> ContainerBitMap -> Full
|
|
//
|
|
// Throughout the code it is assumed (and checked) that the last two bits of the encoding
|
|
// for Howl (0b11) is assumed to be the same as the last two bits for "FullCardSet"; this
|
|
// has been done in various places to not be required to check for a "FullCardSet" first
|
|
// all the time in iteration code (only if there is a Howl card set container, that is
|
|
// fairly uncommon).
|
|
using ContainerPtr = void*;
|
|
static const uintptr_t ContainerInlinePtr = 0x0;
|
|
static const uintptr_t ContainerArrayOfCards = 0x1;
|
|
static const uintptr_t ContainerBitMap = 0x2;
|
|
static const uintptr_t ContainerHowl = 0x3;
|
|
|
|
// The special sentinel values
|
|
static constexpr ContainerPtr FreeCardSet = nullptr;
|
|
// Unfortunately we can't make (G1CardSet::ContainerPtr)-1 constexpr because
|
|
// reinterpret_casts are forbidden in constexprs. Use a regular static instead.
|
|
static ContainerPtr FullCardSet;
|
|
|
|
static const uintptr_t ContainerPtrTypeMask = ((uintptr_t)1 << ContainerPtrHeaderSize) - 1;
|
|
|
|
static ContainerPtr strip_container_type(ContainerPtr ptr) { return (ContainerPtr)((uintptr_t)ptr & ~ContainerPtrTypeMask); }
|
|
|
|
static uint container_type(ContainerPtr ptr) { return (uintptr_t)ptr & ContainerPtrTypeMask; }
|
|
|
|
template <class T>
|
|
static T* container_ptr(ContainerPtr ptr);
|
|
|
|
private:
|
|
G1CardSetMemoryManager* _mm;
|
|
G1CardSetConfiguration* _config;
|
|
|
|
G1CardSetHashTable* _table;
|
|
|
|
// Total number of cards in this card set. This is a best-effort value, i.e. there may
|
|
// be (slightly) more cards in the card set than this value in reality.
|
|
Atomic<size_t> _num_occupied;
|
|
|
|
ContainerPtr make_container_ptr(void* value, uintptr_t type);
|
|
|
|
ContainerPtr acquire_container(Atomic<ContainerPtr>* container_addr);
|
|
// Returns true if the card set container should be released
|
|
bool release_container(ContainerPtr container);
|
|
// Release card set and free if needed.
|
|
void release_and_maybe_free_container(ContainerPtr container);
|
|
// Release card set and free (and it must be freeable).
|
|
void release_and_must_free_container(ContainerPtr container);
|
|
|
|
// Coarsens the card set container cur_container to the next level; tries to replace the
|
|
// previous ContainerPtr with a new one which includes the given card_in_region.
|
|
// coarsen_container does not transfer cards from cur_container
|
|
// to the new container. Transfer is achieved by transfer_cards.
|
|
// Returns true if this was the thread that coarsened the container (and added the card).
|
|
bool coarsen_container(Atomic<ContainerPtr>* container_addr,
|
|
ContainerPtr cur_container,
|
|
uint card_in_region, bool within_howl = false);
|
|
|
|
ContainerPtr create_coarsened_array_of_cards(uint card_in_region, bool within_howl);
|
|
|
|
// Transfer entries from source_card_set to a recently installed coarser storage type
|
|
// We only need to transfer anything finer than ContainerBitMap. "Full" contains
|
|
// all elements anyway.
|
|
void transfer_cards(G1CardSetHashTableValue* table_entry, ContainerPtr source_container, uint card_region);
|
|
void transfer_cards_in_howl(ContainerPtr parent_container, ContainerPtr source_container, uint card_region);
|
|
|
|
G1AddCardResult add_to_container(Atomic<ContainerPtr>* container_addr, ContainerPtr container, uint card_region, uint card, bool increment_total = true);
|
|
|
|
G1AddCardResult add_to_inline_ptr(Atomic<ContainerPtr>* container_addr, ContainerPtr container, uint card_in_region);
|
|
G1AddCardResult add_to_array(ContainerPtr container, uint card_in_region);
|
|
G1AddCardResult add_to_bitmap(ContainerPtr container, uint card_in_region);
|
|
G1AddCardResult add_to_howl(ContainerPtr parent_container, uint card_region, uint card_in_region, bool increment_total = true);
|
|
|
|
G1CardSetHashTableValue* get_or_add_container(uint card_region, bool* should_grow_table);
|
|
G1CardSetHashTableValue* get_container(uint card_region);
|
|
|
|
// Iterate over cards of a card set container during transfer of the cards from
|
|
// one container to another. Executes
|
|
//
|
|
// void operator ()(uint card_idx)
|
|
//
|
|
// on the given class.
|
|
template <class CardVisitor>
|
|
void iterate_cards_during_transfer(ContainerPtr const container, CardVisitor& vl);
|
|
|
|
uint container_type_to_mem_object_type(uintptr_t type) const;
|
|
uint8_t* allocate_mem_object(uintptr_t type);
|
|
void free_mem_object(ContainerPtr container);
|
|
|
|
void split_card(uintptr_t card, uint& card_region, uint& card_within_region) const;
|
|
|
|
G1AddCardResult add_card(uint card_region, uint card_in_region, bool increment_total = true);
|
|
|
|
bool contains_card(uint card_region, uint card_in_region);
|
|
|
|
// Testing API
|
|
class CardClosure {
|
|
public:
|
|
virtual void do_card(uint region_idx, uint card_idx) = 0;
|
|
};
|
|
|
|
void iterate_cards(CardClosure& cl);
|
|
|
|
public:
|
|
G1CardSetConfiguration* config() const { return _config; }
|
|
|
|
// Create a new remembered set for a particular heap region.
|
|
G1CardSet(G1CardSetConfiguration* config, G1CardSetMemoryManager* mm);
|
|
virtual ~G1CardSet();
|
|
|
|
static void initialize(MemRegion reserved);
|
|
|
|
// Adds the given card to this set, returning an appropriate result.
|
|
// If incremental_count is true and the card has been added, updates the total count.
|
|
G1AddCardResult add_card(uintptr_t card);
|
|
|
|
bool contains_card(uintptr_t card);
|
|
|
|
void print_info(outputStream* st, uintptr_t card);
|
|
|
|
// Returns whether this remembered set (and all sub-sets) have an occupancy
|
|
// that is less or equal to the given occupancy.
|
|
bool occupancy_less_or_equal_to(size_t limit) const;
|
|
|
|
// Returns whether this remembered set (and all sub-sets) does not contain any entry.
|
|
bool is_empty() const;
|
|
|
|
// Returns the number of cards contained in this remembered set.
|
|
size_t occupied() const;
|
|
|
|
size_t num_containers();
|
|
|
|
static void print_coarsen_stats(outputStream* out);
|
|
|
|
// Returns size of the actual remembered set containers in bytes.
|
|
size_t mem_size() const;
|
|
size_t unused_mem_size() const;
|
|
// Returns the size of static data in bytes.
|
|
static size_t static_mem_size();
|
|
|
|
// Clear the entire contents of this remembered set.
|
|
void clear();
|
|
|
|
void reset_table_scanner();
|
|
|
|
void reset_table_scanner_for_groups();
|
|
|
|
// Iterate over the container, calling a method on every card or card range contained
|
|
// in the card container.
|
|
// For every container, first calls
|
|
//
|
|
// void start_iterate(uint tag, uint region_idx);
|
|
//
|
|
// Then for every card or card range it calls
|
|
//
|
|
// void do_card(uint card_idx);
|
|
// void do_card_range(uint card_idx, uint length);
|
|
//
|
|
// where card_idx is the card index within that region_idx passed before in
|
|
// start_iterate().
|
|
//
|
|
template <class CardOrRangeVisitor>
|
|
void iterate_cards_or_ranges_in_container(ContainerPtr const container, CardOrRangeVisitor& cl);
|
|
|
|
class ContainerPtrClosure {
|
|
public:
|
|
virtual void do_containerptr(uint card_region_idx, size_t num_occupied, ContainerPtr container) = 0;
|
|
};
|
|
|
|
void iterate_containers(ContainerPtrClosure* cl, bool safepoint = false);
|
|
};
|
|
|
|
class G1CardSetHashTableValue {
|
|
public:
|
|
using ContainerPtr = G1CardSet::ContainerPtr;
|
|
|
|
const uint _region_idx;
|
|
Atomic<uint> _num_occupied;
|
|
Atomic<ContainerPtr> _container;
|
|
|
|
// Copy constructor needed for use in ConcurrentHashTable.
|
|
G1CardSetHashTableValue(const G1CardSetHashTableValue& other) :
|
|
_region_idx(other._region_idx),
|
|
_num_occupied(other._num_occupied.load_relaxed()),
|
|
_container(other._container.load_relaxed())
|
|
{ }
|
|
|
|
G1CardSetHashTableValue(uint region_idx, ContainerPtr container) : _region_idx(region_idx), _num_occupied(0), _container(container) { }
|
|
};
|
|
|
|
#endif // SHARE_GC_G1_G1CARDSET_HPP
|