/* * Copyright (c) 2021, 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. *
*/
assert(_inline_ptr_bits_per_card <= G1CardSetContainer::LogCardsPerRegionLimit, "inline_ptr_bits_per_card (%u) is wasteful, can represent more than maximum possible card indexes (%u)",
_inline_ptr_bits_per_card, G1CardSetContainer::LogCardsPerRegionLimit);
assert(_inline_ptr_bits_per_card >= _log2_cards_per_card_region, "inline_ptr_bits_per_card (%u) must be larger than possible card indexes (%u)",
_inline_ptr_bits_per_card, _log2_cards_per_card_region);
assert(cards_in_bitmap_threshold_percent >= 0.0 && cards_in_bitmap_threshold_percent <= 1.0, "cards_in_bitmap_threshold_percent (%1.2f) out of range", cards_in_bitmap_threshold_percent);
assert(cards_in_howl_threshold_percent >= 0.0 && cards_in_howl_threshold_percent <= 1.0, "cards_in_howl_threshold_percent (%1.2f) out of range", cards_in_howl_threshold_percent);
assert(is_power_of_2(_max_cards_in_card_set), "max_cards_in_card_set must be a power of 2: %u", _max_cards_in_card_set);
assert(_max_cards_in_card_set <= G1CardSetContainer::cards_per_region_limit(), "Specified number of cards (%u) exceeds maximum representable (%u)",
_max_cards_in_card_set, G1CardSetContainer::cards_per_region_limit());
assert(_cards_in_howl_bitmap_threshold <= _max_cards_in_howl_bitmap, "Threshold to coarsen Howl Bitmap to Howl Full (%u) must be " "smaller than or equal to max number of cards in Howl bitmap (%u)",
_cards_in_howl_bitmap_threshold, _max_cards_in_howl_bitmap);
assert(_cards_in_howl_threshold <= _max_cards_in_card_set, "Threshold to coarsen Howl to Full (%u) must be " "smaller than or equal to max number of cards in card region (%u)",
_cards_in_howl_threshold, _max_cards_in_card_set);
if (!_inserted_card && inserted) { // It does not matter to us who is setting the flag so a regular atomic store // is sufficient.
Atomic::store(&_inserted_card, true);
}
// Check if the number of cards within a region fits an uint. if (CardBitsWithinCardRegion > BitsInUint) {
vm_exit_during_initialization("Can not represent all cards in a card region within uint.");
}
// Check if the card region/region within cards combination can cover the heap. const uint HeapSizeBits = log2i_exact(round_up_power_of_2(reserved.byte_size())); if (HeapSizeBits > (BitsInUint + _split_card_shift + G1CardTable::card_shift())) {
FormatBuffer<> fmt("Can not represent all cards in the heap with card region/card within region. " "Heap %zuB (%u bits) Card set only covers %u bits.",
reserved.byte_size(),
HeapSizeBits,
BitsInUint + _split_card_shift + G1CardTable::card_shift());
vm_exit_during_initialization(fmt, "Decrease heap size.");
}
}
uint G1CardSet::container_type_to_mem_object_type(uintptr_t type) const {
assert(type == G1CardSet::ContainerArrayOfCards ||
type == G1CardSet::ContainerBitMap ||
type == G1CardSet::ContainerHowl, "should not allocate container type %zu", type);
void G1CardSet::free_mem_object(ContainerPtr container) {
assert(container != G1CardSet::FreeCardSet, "should not free container FreeCardSet");
assert(container != G1CardSet::FullCardSet, "should not free container FullCardSet");
uintptr_t type = container_type(container); void* value = strip_container_type(container);
assert(type == G1CardSet::ContainerArrayOfCards ||
type == G1CardSet::ContainerBitMap ||
type == G1CardSet::ContainerHowl, "should not free card set type %zu", type);
assert(static_cast<G1CardSetContainer*>(value)->refcount() == 1, "must be");
G1CardSet::ContainerPtr G1CardSet::acquire_container(ContainerPtr volatile* container_addr) { // Update reference counts under RCU critical section to avoid a // use-after-cleapup bug where we increment a reference count for // an object whose memory has already been cleaned up and reused.
GlobalCounter::CriticalSection cs(Thread::current()); while (true) { // Get ContainerPtr and increment refcount atomically wrt to memory reuse.
ContainerPtr container = Atomic::load_acquire(container_addr);
uint cs_type = container_type(container); if (container == FullCardSet || cs_type == ContainerInlinePtr) { return container;
}
void G1CardSet::release_and_maybe_free_container(ContainerPtr container) { if (release_container(container)) {
free_mem_object(container);
}
}
void G1CardSet::release_and_must_free_container(ContainerPtr container) { bool should_free = release_container(container);
assert(should_free, "should have been the only one having a reference");
free_mem_object(container);
}
class G1ReleaseCardsets : public StackObj {
G1CardSet* _card_set; using ContainerPtr = G1CardSet::ContainerPtr;
if (add_result != Overflow) { break;
} // Card set container has overflown. Coarsen or retry. bool coarsened = coarsen_container(bucket_entry, container, card_in_region, true/* within_howl */);
_coarsen_stats.record_coarsening(container_type(container) + G1CardSetCoarsenStats::CoarsenHowlOffset, !coarsened); if (coarsened) { // We successful coarsened this card set container (and in the process added the card).
add_result = Added;
to_transfer = container; break;
} // Somebody else beat us to coarsening. Retry.
release_and_maybe_free_container(container);
}
if (increment_total && add_result == Added) {
Atomic::inc(&howl->_num_entries, memory_order_relaxed);
}
if (to_transfer != nullptr) {
transfer_cards_in_howl(parent_container, to_transfer, card_region);
}
switch (container_type(cur_container)) { case ContainerArrayOfCards: {
new_container = create_coarsened_array_of_cards(card_in_region, within_howl); break;
} case ContainerBitMap: {
new_container = FullCardSet; break;
} case ContainerInlinePtr: {
uint const size = _config->max_cards_in_array();
uint8_t* data = allocate_mem_object(ContainerArrayOfCards); new (data) G1CardSetArray(card_in_region, size);
new_container = make_container_ptr(data, ContainerArrayOfCards); break;
} case ContainerHowl: {
new_container = FullCardSet; // anything will do at this point. break;
} default:
ShouldNotReachHere();
}
ContainerPtr old_value = Atomic::cmpxchg(container_addr, cur_container, new_container); // Memory order? if (old_value == cur_container) { // Success. Indicate that the cards from the current card set must be transferred // by this caller. // Release the hash table reference to the card. The caller still holds the // reference to this card set, so it can never be released (and we do not need to // check its result). bool should_free = release_container(cur_container);
assert(!should_free, "must have had more than one reference"); // Free containers if cur_container is ContainerHowl if (container_type(cur_container) == ContainerHowl) {
G1ReleaseCardsets rel(this);
container_ptr<G1CardSetHowl>(cur_container)->iterate(rel, _config->num_buckets_in_howl());
} returntrue;
} else { // Somebody else beat us to coarsening that card set. Exit, but clean up first. if (new_container != FullCardSet) {
assert(new_container != nullptr, "must not be");
release_and_must_free_container(new_container);
} returnfalse;
}
}
class G1TransferCard : public StackObj {
G1CardSet* _card_set;
uint _region_idx; public:
G1TransferCard(G1CardSet* card_set, uint region_idx) : _card_set(card_set), _region_idx(region_idx) { }
void G1CardSet::transfer_cards(G1CardSetHashTableValue* table_entry, ContainerPtr source_container, uint card_region) {
assert(source_container != FullCardSet, "Should not need to transfer from FullCardSet"); // Need to transfer old entries unless there is a Full card set container in place now, i.e. // the old type has been ContainerBitMap. "Full" contains all elements anyway. if (container_type(source_container) != ContainerHowl) {
G1TransferCard iter(this, card_region);
iterate_cards_during_transfer(source_container, iter);
} else {
assert(container_type(source_container) == ContainerHowl, "must be"); // Need to correct for that the Full remembered set occupies more cards than the // AoCS before.
Atomic::add(&_num_occupied, _config->max_cards_in_region() - table_entry->_num_occupied, memory_order_relaxed);
}
}
void G1CardSet::transfer_cards_in_howl(ContainerPtr parent_container,
ContainerPtr source_container,
uint card_region) {
assert(container_type(parent_container) == ContainerHowl, "must be");
assert(source_container != FullCardSet, "Should not need to transfer from full"); // Need to transfer old entries unless there is a Full card set in place now, i.e. // the old type has been ContainerBitMap. if (container_type(source_container) != ContainerBitMap) { // We only need to transfer from anything below ContainerBitMap.
G1TransferCard iter(this, card_region);
iterate_cards_during_transfer(source_container, iter);
} else {
uint diff = _config->max_cards_in_howl_bitmap() - container_ptr<G1CardSetBitMap>(source_container)->num_bits_set();
// Need to correct for that the Full remembered set occupies more cards than the // bitmap before. // We add 1 card less because the values will be incremented // in G1CardSet::add_card for the current addition or where already incremented in // G1CardSet::add_to_howl after coarsening.
diff -= 1;
if (add_result != Overflow) { break;
} // Card set has overflown. Coarsen or retry. bool coarsened = coarsen_container(&table_entry->_container, container, card_in_region);
_coarsen_stats.record_coarsening(container_type(container), !coarsened); if (coarsened) { // We successful coarsened this card set container (and in the process added the card).
add_result = Added;
to_transfer = container; break;
} // Somebody else beat us to coarsening. Retry.
release_and_maybe_free_container(container);
}
if (increment_total && add_result == Added) {
Atomic::inc(&table_entry->_num_occupied, memory_order_relaxed);
Atomic::inc(&_num_occupied, memory_order_relaxed);
} if (should_grow_table) {
_table->grow();
} if (to_transfer != nullptr) {
transfer_cards(table_entry, to_transfer, card_region);
}
release_and_maybe_free_container(container);
return add_result;
}
bool G1CardSet::contains_card(uint card_region, uint card_in_region) {
assert(card_in_region < _config->max_cards_in_region(), "Card %u is beyond max %u", card_in_region, _config->max_cards_in_region());
// Protect the card set container from reclamation.
GlobalCounter::CriticalSection cs(Thread::current());
G1CardSetHashTableValue* table_entry = get_container(card_region); if (table_entry == nullptr) { returnfalse;
}
ContainerPtr container = table_entry->_container; if (container == FullCardSet) { // contains_card() is not a performance critical method so we do not hide that // case in the switch below. returntrue;
}
switch (container_type(container)) { case ContainerInlinePtr: {
G1CardSetInlinePtr ptr(container); return ptr.contains(card_in_region, _config->inline_ptr_bits_per_card());
} case ContainerArrayOfCards: return container_ptr<G1CardSetArray>(container)->contains(card_in_region); case ContainerBitMap: return container_ptr<G1CardSetBitMap>(container)->contains(card_in_region, _config->max_cards_in_howl_bitmap()); case ContainerHowl: {
G1CardSetHowl* howling_array = container_ptr<G1CardSetHowl>(container);
ContainerPtr container = table_entry->_container; if (container == FullCardSet) {
st->print("FULL card set)"); return;
} switch (container_type(container)) { case ContainerInlinePtr: {
st->print("InlinePtr not containing %u", card_in_region); break;
} case ContainerArrayOfCards: {
st->print("AoC not containing %u", card_in_region); break;
} case ContainerBitMap: {
st->print("BitMap not containing %u", card_in_region); break;
} case ContainerHowl: {
st->print("ContainerHowl not containing %u", card_in_region); break;
} default: st->print("Unknown card set container type %u", container_type(container)); ShouldNotReachHere(); break;
}
}
template <class CardVisitor> void G1CardSet::iterate_cards_during_transfer(ContainerPtr const container, CardVisitor& cl) {
uint type = container_type(container);
assert(type == ContainerInlinePtr || type == ContainerArrayOfCards, "invalid card set type %d to transfer from",
container_type(container));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.