8332517: G1: Refactor G1AllocRegion

Reviewed-by: tschatzl, ayang
This commit is contained in:
Ivan Walulya 2024-07-03 15:12:40 +00:00
parent 5a8af2b8b9
commit cf4f2b53d6
6 changed files with 99 additions and 147 deletions

View File

@ -100,16 +100,13 @@ size_t G1AllocRegion::retire_internal(G1HeapRegion* alloc_region, bool fill_up)
// it will never be empty.
size_t waste = 0;
assert_alloc_region(!alloc_region->is_empty(),
"the alloc region should never be empty");
"the alloc region should never be empty");
if (fill_up) {
waste = fill_up_remaining_space(alloc_region);
}
assert_alloc_region(alloc_region->used() >= _used_bytes_before, "invariant");
size_t allocated_bytes = alloc_region->used() - _used_bytes_before;
retire_region(alloc_region, allocated_bytes);
_used_bytes_before = 0;
retire_region(alloc_region);
return waste;
}
@ -132,15 +129,14 @@ size_t G1AllocRegion::retire(bool fill_up) {
HeapWord* G1AllocRegion::new_alloc_region_and_allocate(size_t word_size) {
assert_alloc_region(_alloc_region == _dummy_region, "pre-condition");
assert_alloc_region(_used_bytes_before == 0, "pre-condition");
trace("attempting region allocation");
G1HeapRegion* new_alloc_region = allocate_new_region(word_size);
if (new_alloc_region != nullptr) {
new_alloc_region->reset_pre_dummy_top();
// Need to do this before the allocation
_used_bytes_before = new_alloc_region->used();
HeapWord* result = allocate(new_alloc_region, word_size);
assert(new_alloc_region->is_empty(), "new regions should be empty");
HeapWord* result = new_alloc_region->allocate(word_size);
assert_alloc_region(result != nullptr, "the allocation should succeeded");
OrderAccess::storestore();
@ -159,7 +155,7 @@ HeapWord* G1AllocRegion::new_alloc_region_and_allocate(size_t word_size) {
void G1AllocRegion::init() {
trace("initializing");
assert_alloc_region(_alloc_region == nullptr && _used_bytes_before == 0, "pre-condition");
assert_alloc_region(_alloc_region == nullptr, "pre-condition");
assert_alloc_region(_dummy_region != nullptr, "should have been set");
_alloc_region = _dummy_region;
_count = 0;
@ -168,16 +164,9 @@ void G1AllocRegion::init() {
void G1AllocRegion::set(G1HeapRegion* alloc_region) {
trace("setting");
// We explicitly check that the region is not empty to make sure we
// maintain the "the alloc region cannot be empty" invariant.
assert_alloc_region(alloc_region != nullptr && !alloc_region->is_empty(), "pre-condition");
assert_alloc_region(_alloc_region == _dummy_region &&
_used_bytes_before == 0 && _count == 0,
"pre-condition");
assert_alloc_region(_alloc_region == _dummy_region && _count == 0, "pre-condition");
_used_bytes_before = alloc_region->used();
_alloc_region = alloc_region;
_count += 1;
update_alloc_region(alloc_region);
trace("set");
}
@ -237,7 +226,7 @@ void G1AllocRegion::trace(const char* str, size_t min_word_size, size_t desired_
if (detailed_info) {
if (result != nullptr) {
out->print(" min " SIZE_FORMAT " desired " SIZE_FORMAT " actual " SIZE_FORMAT " " PTR_FORMAT,
min_word_size, desired_word_size, actual_word_size, p2i(result));
min_word_size, desired_word_size, actual_word_size, p2i(result));
} else if (min_word_size != 0) {
out->print(" min " SIZE_FORMAT " desired " SIZE_FORMAT, min_word_size, desired_word_size);
}
@ -252,7 +241,6 @@ G1AllocRegion::G1AllocRegion(const char* name,
uint node_index)
: _alloc_region(nullptr),
_count(0),
_used_bytes_before(0),
_name(name),
_node_index(node_index)
{ }
@ -261,9 +249,8 @@ G1HeapRegion* MutatorAllocRegion::allocate_new_region(size_t word_size) {
return _g1h->new_mutator_alloc_region(word_size, _node_index);
}
void MutatorAllocRegion::retire_region(G1HeapRegion* alloc_region,
size_t allocated_bytes) {
_g1h->retire_mutator_alloc_region(alloc_region, allocated_bytes);
void MutatorAllocRegion::retire_region(G1HeapRegion* alloc_region) {
_g1h->retire_mutator_alloc_region(alloc_region, alloc_region->used());
}
void MutatorAllocRegion::init() {
@ -346,9 +333,11 @@ G1HeapRegion* G1GCAllocRegion::allocate_new_region(size_t word_size) {
return _g1h->new_gc_alloc_region(word_size, _purpose, _node_index);
}
void G1GCAllocRegion::retire_region(G1HeapRegion* alloc_region,
size_t allocated_bytes) {
void G1GCAllocRegion::retire_region(G1HeapRegion* alloc_region) {
assert(alloc_region->used() >= _used_bytes_before, "invariant");
size_t allocated_bytes = alloc_region->used() - _used_bytes_before;
_g1h->retire_gc_alloc_region(alloc_region, allocated_bytes, _purpose);
_used_bytes_before = 0;
}
size_t G1GCAllocRegion::retire(bool fill_up) {
@ -360,3 +349,8 @@ size_t G1GCAllocRegion::retire(bool fill_up) {
}
return end_waste;
}
void G1GCAllocRegion::reuse(G1HeapRegion* alloc_region) {
_used_bytes_before = alloc_region->used();
set(alloc_region);
}

View File

@ -63,11 +63,6 @@ private:
// distinct regions this object can used during an active interval.
uint _count;
// When we set up a new active region we save its used bytes in this
// field so that, when we retire it, we can calculate how much space
// we allocated in it.
size_t _used_bytes_before;
// Useful for debugging and tracing.
const char* _name;
@ -94,24 +89,14 @@ protected:
// The memory node index this allocation region belongs to.
uint _node_index;
void set(G1HeapRegion* alloc_region);
// Reset the alloc region to point the dummy region.
void reset_alloc_region();
// Perform a non-MT-safe allocation out of the given region.
inline HeapWord* allocate(G1HeapRegion* alloc_region,
size_t word_size);
// Perform a MT-safe allocation out of the given region.
inline HeapWord* par_allocate(G1HeapRegion* alloc_region,
size_t word_size);
// Perform a MT-safe allocation out of the given region, with the given
// minimum and desired size. Returns the actual size allocated (between
// minimum and desired size) in actual_word_size if the allocation has been
// successful.
inline HeapWord* par_allocate(G1HeapRegion* alloc_region,
size_t min_word_size,
size_t desired_word_size,
size_t* actual_word_size);
// Ensure that the region passed as a parameter has been filled up
// so that no one else can allocate out of it any more.
@ -131,8 +116,7 @@ protected:
static G1CollectedHeap* _g1h;
virtual G1HeapRegion* allocate_new_region(size_t word_size) = 0;
virtual void retire_region(G1HeapRegion* alloc_region,
size_t allocated_bytes) = 0;
virtual void retire_region(G1HeapRegion* alloc_region) = 0;
G1AllocRegion(const char* name, bool bot_updates, uint node_index);
@ -173,12 +157,6 @@ public:
// Should be called before we start using this object.
virtual void init();
// This can be used to set the active region to a specific
// region. (Use Example: we try to retain the last old GC alloc
// region that we've used during a GC and we can use set() to
// re-instate it at the beginning of the next GC.)
void set(G1HeapRegion* alloc_region);
// Should be called when we want to release the active region which
// is returned after it's been retired.
virtual G1HeapRegion* release();
@ -205,10 +183,9 @@ private:
// in it and the free size in the currently retained region, if any.
bool should_retain(G1HeapRegion* region);
protected:
virtual G1HeapRegion* allocate_new_region(size_t word_size);
virtual void retire_region(G1HeapRegion* alloc_region, size_t allocated_bytes);
virtual size_t retire(bool fill_up);
G1HeapRegion* allocate_new_region(size_t word_size) override;
void retire_region(G1HeapRegion* alloc_region) override;
size_t retire(bool fill_up) override;
public:
MutatorAllocRegion(uint node_index)
: G1AllocRegion("Mutator Alloc Region", false /* bot_updates */, node_index),
@ -231,27 +208,36 @@ public:
// This specialization of release() makes sure that the retained alloc
// region is retired and set to null.
virtual G1HeapRegion* release();
G1HeapRegion* release() override;
virtual void init();
void init() override;
};
// Common base class for allocation regions used during GC.
class G1GCAllocRegion : public G1AllocRegion {
// When we set up a new active region we save its used bytes in this
// field so that, when we retire it, we can calculate how much space
// we allocated in it.
size_t _used_bytes_before;
protected:
G1EvacStats* _stats;
G1HeapRegionAttr::region_type_t _purpose;
virtual G1HeapRegion* allocate_new_region(size_t word_size);
virtual void retire_region(G1HeapRegion* alloc_region, size_t allocated_bytes);
G1HeapRegion* allocate_new_region(size_t word_size) override;
void retire_region(G1HeapRegion* alloc_region) override;
virtual size_t retire(bool fill_up);
size_t retire(bool fill_up) override;
G1GCAllocRegion(const char* name, bool bot_updates, G1EvacStats* stats,
G1HeapRegionAttr::region_type_t purpose, uint node_index = G1NUMA::AnyNodeIndex)
: G1AllocRegion(name, bot_updates, node_index), _stats(stats), _purpose(purpose) {
: G1AllocRegion(name, bot_updates, node_index), _used_bytes_before(0), _stats(stats), _purpose(purpose) {
assert(stats != nullptr, "Must pass non-null PLAB statistics");
}
public:
// This can be used to reuse a specific region. (Use Example: we try to retain the
// last old GC alloc region that we've used during a GC and we can use reuse() to
// re-instate it at the beginning of the next GC.)
void reuse(G1HeapRegion* alloc_region);
};
class SurvivorGCAllocRegion : public G1GCAllocRegion {

View File

@ -31,9 +31,9 @@
#define assert_alloc_region(p, message) \
do { \
assert((p), "[%s] %s c: %u r: " PTR_FORMAT " u: " SIZE_FORMAT, \
_name, (message), _count, p2i(_alloc_region), \
_used_bytes_before); \
assert((p), "[%s] %s c: %u r: " PTR_FORMAT, \
_name, (message), _count, p2i(_alloc_region) \
); \
} while (0)
@ -41,41 +41,27 @@ inline void G1AllocRegion::reset_alloc_region() {
_alloc_region = _dummy_region;
}
inline HeapWord* G1AllocRegion::allocate(G1HeapRegion* alloc_region,
size_t word_size) {
assert(alloc_region != nullptr, "pre-condition");
return alloc_region->allocate(word_size);
}
inline HeapWord* G1AllocRegion::par_allocate(G1HeapRegion* alloc_region, size_t word_size) {
size_t temp;
return par_allocate(alloc_region, word_size, word_size, &temp);
}
inline HeapWord* G1AllocRegion::par_allocate(G1HeapRegion* alloc_region,
size_t min_word_size,
size_t desired_word_size,
size_t* actual_word_size) {
assert(alloc_region != nullptr, "pre-condition");
assert(!alloc_region->is_empty(), "pre-condition");
return alloc_region->par_allocate(min_word_size, desired_word_size, actual_word_size);
size_t temp;
return alloc_region->par_allocate(word_size, word_size, &temp);
}
inline HeapWord* G1AllocRegion::attempt_allocation(size_t min_word_size,
size_t desired_word_size,
size_t* actual_word_size) {
G1HeapRegion* alloc_region = _alloc_region;
assert_alloc_region(alloc_region != nullptr, "not initialized properly");
assert_alloc_region(alloc_region != nullptr && !alloc_region->is_empty(), "not initialized properly");
HeapWord* result = alloc_region->par_allocate(min_word_size, desired_word_size, actual_word_size);
HeapWord* result = par_allocate(alloc_region, min_word_size, desired_word_size, actual_word_size);
if (result != nullptr) {
trace("alloc", min_word_size, desired_word_size, *actual_word_size, result);
return result;
} else {
trace("alloc failed", min_word_size, desired_word_size);
}
trace("alloc failed", min_word_size, desired_word_size);
return nullptr;
return result;
}
inline HeapWord* G1AllocRegion::attempt_allocation_locked(size_t word_size) {
@ -112,7 +98,7 @@ inline HeapWord* MutatorAllocRegion::attempt_retained_allocation(size_t min_word
size_t desired_word_size,
size_t* actual_word_size) {
if (_retained_alloc_region != nullptr) {
HeapWord* result = par_allocate(_retained_alloc_region, min_word_size, desired_word_size, actual_word_size);
HeapWord* result = _retained_alloc_region->par_allocate(min_word_size, desired_word_size, actual_word_size);
if (result != nullptr) {
trace("alloc retained", min_word_size, desired_word_size, *actual_word_size, result);
return result;

View File

@ -118,7 +118,7 @@ void G1Allocator::reuse_retained_old_region(G1EvacInfo* evacuation_info,
// we allocate to in the region sets. We'll re-add it later, when
// it's retired again.
_g1h->old_set_remove(retained_region);
old->set(retained_region);
old->reuse(retained_region);
G1HeapRegionPrinter::reuse(retained_region);
evacuation_info->set_alloc_regions_used_before(retained_region->used());
}

View File

@ -127,18 +127,6 @@ private:
void mangle_unused_area() PRODUCT_RETURN;
// Try to allocate at least min_word_size and up to desired_size from this region.
// Returns null if not possible, otherwise sets actual_word_size to the amount of
// space allocated.
// This version assumes that all allocation requests to this G1HeapRegion are properly
// synchronized.
inline HeapWord* allocate_impl(size_t min_word_size, size_t desired_word_size, size_t* actual_word_size);
// Try to allocate at least min_word_size and up to desired_size from this G1HeapRegion.
// Returns null if not possible, otherwise sets actual_word_size to the amount of
// space allocated.
// This version synchronizes with other calls to par_allocate_impl().
inline HeapWord* par_allocate_impl(size_t min_word_size, size_t desired_word_size, size_t* actual_word_size);
inline HeapWord* advance_to_block_containing_addr(const void* addr,
HeapWord* const pb,
HeapWord* first_block) const;
@ -163,8 +151,18 @@ public:
// All allocations are done without updating the BOT. The BOT
// needs to be kept in sync for old generation regions and
// this is done by explicit updates when crossing thresholds.
// Try to allocate at least min_word_size and up to desired_size from this HeapRegion.
// Returns null if not possible, otherwise sets actual_word_size to the amount of
// space allocated.
// This version synchronizes with other calls to par_allocate().
inline HeapWord* par_allocate(size_t min_word_size, size_t desired_word_size, size_t* word_size);
inline HeapWord* allocate(size_t word_size);
// Try to allocate at least min_word_size and up to desired_size from this region.
// Returns null if not possible, otherwise sets actual_word_size to the amount of
// space allocated.
// This version assumes that all allocation requests to this HeapRegion are properly
// synchronized.
inline HeapWord* allocate(size_t min_word_size, size_t desired_word_size, size_t* actual_size);
// Full GC support methods.

View File

@ -42,47 +42,6 @@
#include "utilities/align.hpp"
#include "utilities/globalDefinitions.hpp"
inline HeapWord* G1HeapRegion::allocate_impl(size_t min_word_size,
size_t desired_word_size,
size_t* actual_size) {
HeapWord* obj = top();
size_t available = pointer_delta(end(), obj);
size_t want_to_allocate = MIN2(available, desired_word_size);
if (want_to_allocate >= min_word_size) {
HeapWord* new_top = obj + want_to_allocate;
set_top(new_top);
assert(is_object_aligned(obj) && is_object_aligned(new_top), "checking alignment");
*actual_size = want_to_allocate;
return obj;
} else {
return nullptr;
}
}
inline HeapWord* G1HeapRegion::par_allocate_impl(size_t min_word_size,
size_t desired_word_size,
size_t* actual_size) {
do {
HeapWord* obj = top();
size_t available = pointer_delta(end(), obj);
size_t want_to_allocate = MIN2(available, desired_word_size);
if (want_to_allocate >= min_word_size) {
HeapWord* new_top = obj + want_to_allocate;
HeapWord* result = Atomic::cmpxchg(&_top, obj, new_top);
// result can be one of two:
// the old top value: the exchange succeeded
// otherwise: the new value of the top is returned.
if (result == obj) {
assert(is_object_aligned(obj) && is_object_aligned(new_top), "checking alignment");
*actual_size = want_to_allocate;
return obj;
}
} else {
return nullptr;
}
} while (true);
}
inline HeapWord* G1HeapRegion::block_start(const void* addr) const {
return block_start(addr, parsable_bottom_acquire());
}
@ -225,9 +184,27 @@ inline void G1HeapRegion::apply_to_marked_objects(G1CMBitMap* bitmap, ApplyToMar
}
inline HeapWord* G1HeapRegion::par_allocate(size_t min_word_size,
size_t desired_word_size,
size_t* actual_word_size) {
return par_allocate_impl(min_word_size, desired_word_size, actual_word_size);
size_t desired_word_size,
size_t* actual_word_size) {
do {
HeapWord* obj = top();
size_t available = pointer_delta(end(), obj);
size_t want_to_allocate = MIN2(available, desired_word_size);
if (want_to_allocate >= min_word_size) {
HeapWord* new_top = obj + want_to_allocate;
HeapWord* result = Atomic::cmpxchg(&_top, obj, new_top);
// result can be one of two:
// the old top value: the exchange succeeded
// otherwise: the new value of the top is returned.
if (result == obj) {
assert(is_object_aligned(obj) && is_object_aligned(new_top), "checking alignment");
*actual_word_size = want_to_allocate;
return obj;
}
} else {
return nullptr;
}
} while (true);
}
inline HeapWord* G1HeapRegion::allocate(size_t word_size) {
@ -236,9 +213,20 @@ inline HeapWord* G1HeapRegion::allocate(size_t word_size) {
}
inline HeapWord* G1HeapRegion::allocate(size_t min_word_size,
size_t desired_word_size,
size_t* actual_word_size) {
return allocate_impl(min_word_size, desired_word_size, actual_word_size);
size_t desired_word_size,
size_t* actual_word_size) {
HeapWord* obj = top();
size_t available = pointer_delta(end(), obj);
size_t want_to_allocate = MIN2(available, desired_word_size);
if (want_to_allocate >= min_word_size) {
HeapWord* new_top = obj + want_to_allocate;
set_top(new_top);
assert(is_object_aligned(obj) && is_object_aligned(new_top), "checking alignment");
*actual_word_size = want_to_allocate;
return obj;
} else {
return nullptr;
}
}
inline void G1HeapRegion::update_bot() {