New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
8276098: Do precise BOT updates in G1 evacuation phase #6166
Changes from 3 commits
e48ea28
33b044b
1fba102
98e9244
98fd1e2
6628314
e6ec3bd
df900c6
83049e1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -145,6 +145,16 @@ class G1Allocator : public CHeapObj<mtGC> { | |
uint node_index); | ||
}; | ||
|
||
// Helper class to get the information needed to do | ||
// BOT updates for the end of the PLAB. | ||
class G1PLAB : public PLAB { | ||
public: | ||
G1PLAB(size_t word_sz); | ||
bool is_allocated(); | ||
HeapWord* get_filler(); | ||
size_t get_filler_size(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe instead of two methods that return the remaining space, one that returns a What about making Also the explict call in |
||
}; | ||
|
||
// Manages the PLABs used during garbage collection. Interface for allocation from PLABs. | ||
// Needs to handle multiple contexts, extra alignment in any "survivor" area and some | ||
// statistics. | ||
|
@@ -156,14 +166,27 @@ class G1PLABAllocator : public CHeapObj<mtGC> { | |
G1CollectedHeap* _g1h; | ||
G1Allocator* _allocator; | ||
|
||
PLAB** _alloc_buffers[G1HeapRegionAttr::Num]; | ||
// Region where the current old generation PLAB is allocated. Used to do BOT updates. | ||
HeapRegion* _bot_plab_region; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it is a breakage of abstraction if we only store this information for old gen - we after all allocate the |
||
// Current BOT threshold, a PLAB allocation crossing this threshold will cause a BOT | ||
// update. | ||
HeapWord* _bot_plab_threshold; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is also only interesting for the old generation, isn't it? Same issue as above. |
||
|
||
G1PLAB** _alloc_buffers[G1HeapRegionAttr::Num]; | ||
|
||
// Number of words allocated directly (not counting PLAB allocation). | ||
size_t _direct_allocated[G1HeapRegionAttr::Num]; | ||
|
||
void flush_and_retire_stats(); | ||
inline PLAB* alloc_buffer(G1HeapRegionAttr dest, uint node_index) const; | ||
inline PLAB* alloc_buffer(region_type_t dest, uint node_index) const; | ||
inline G1PLAB* alloc_buffer(G1HeapRegionAttr dest, uint node_index) const; | ||
inline G1PLAB* alloc_buffer(region_type_t dest, uint node_index) const; | ||
|
||
// Helpers to do explicit BOT updates for allocations in old generation regions. | ||
void update_bot_for_direct_allocation(G1HeapRegionAttr attr, HeapWord* addr, size_t size); | ||
void update_bot_for_plab_waste(G1HeapRegionAttr dest, G1PLAB* plab); | ||
// When a new PLAB is allocated a new threshold needs to be calculated and | ||
// possibly also the current region where BOT updates should be done. | ||
void calculate_new_bot_threshold(G1HeapRegionAttr attr, HeapWord* addr); | ||
|
||
// Returns the number of allocation buffers for the given dest. | ||
// There is only 1 buffer for Old while Young may have multiple buffers depending on | ||
|
@@ -199,6 +222,9 @@ class G1PLABAllocator : public CHeapObj<mtGC> { | |
uint node_index); | ||
|
||
void undo_allocation(G1HeapRegionAttr dest, HeapWord* obj, size_t word_sz, uint node_index); | ||
|
||
// Update the BOT for an allocation inside an old PLAB. | ||
void update_bot_for_object(HeapWord* obj_start, size_t obj_size); | ||
}; | ||
|
||
// G1ArchiveAllocator is used to allocate memory in archive | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -86,15 +86,15 @@ inline HeapWord* G1Allocator::attempt_allocation_force(size_t word_size) { | |
return mutator_alloc_region(node_index)->attempt_allocation_force(word_size); | ||
} | ||
|
||
inline PLAB* G1PLABAllocator::alloc_buffer(G1HeapRegionAttr dest, uint node_index) const { | ||
inline G1PLAB* G1PLABAllocator::alloc_buffer(G1HeapRegionAttr dest, uint node_index) const { | ||
assert(dest.is_valid(), | ||
"Allocation buffer index out of bounds: %s", dest.get_type_str()); | ||
assert(_alloc_buffers[dest.type()] != NULL, | ||
"Allocation buffer is NULL: %s", dest.get_type_str()); | ||
return alloc_buffer(dest.type(), node_index); | ||
} | ||
|
||
inline PLAB* G1PLABAllocator::alloc_buffer(region_type_t dest, uint node_index) const { | ||
inline G1PLAB* G1PLABAllocator::alloc_buffer(region_type_t dest, uint node_index) const { | ||
assert(dest < G1HeapRegionAttr::Num, | ||
"Allocation buffer index out of bounds: %u", dest); | ||
|
||
|
@@ -133,4 +133,60 @@ inline HeapWord* G1PLABAllocator::allocate(G1HeapRegionAttr dest, | |
return allocate_direct_or_new_plab(dest, word_sz, refill_failed, node_index); | ||
} | ||
|
||
inline void G1PLABAllocator::update_bot_for_plab_waste(G1HeapRegionAttr attr, G1PLAB* plab) { | ||
if (!attr.is_old()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would prefer to make an extra predicate like |
||
// BOT updates are only done for old generation. | ||
return; | ||
} | ||
|
||
if (!plab->is_allocated()) { | ||
return; | ||
} | ||
update_bot_for_object(plab->get_filler(), plab->get_filler_size()); | ||
} | ||
|
||
inline void G1PLABAllocator::calculate_new_bot_threshold(G1HeapRegionAttr attr, HeapWord* addr) { | ||
if (!attr.is_old()) { | ||
// BOT updates are only done for old generation. | ||
return; | ||
} | ||
|
||
_bot_plab_region = _g1h->heap_region_containing(addr); | ||
_bot_plab_threshold = _bot_plab_region->bot_threshold_for_addr(addr); | ||
|
||
assert(_bot_plab_threshold >= addr, | ||
"threshold must be at or after PLAB start. " PTR_FORMAT " >= " PTR_FORMAT, | ||
p2i(_bot_plab_threshold), p2i(addr)); | ||
assert(_bot_plab_region->is_old(), | ||
"Updating BOT threshold for non-old region. addr: " PTR_FORMAT " region:" HR_FORMAT, | ||
p2i(addr), HR_FORMAT_PARAMS(_bot_plab_region)); | ||
} | ||
|
||
inline void G1PLABAllocator::update_bot_for_direct_allocation(G1HeapRegionAttr attr, HeapWord* addr, size_t size) { | ||
if (!attr.is_old()) { | ||
// BOT updates are only done for old generation. | ||
return; | ||
} | ||
|
||
// Out of PLAB allocations in an old generation region. Update BOT. | ||
HeapRegion* region = _g1h->heap_region_containing(addr); | ||
region->update_bot_at(addr, size); | ||
} | ||
|
||
inline void G1PLABAllocator::update_bot_for_object(HeapWord* obj_start, size_t obj_size) { | ||
HeapWord* obj_end = obj_start + obj_size; | ||
if (obj_end <= _bot_plab_threshold) { | ||
// Not crossing the threshold. | ||
return; | ||
} | ||
|
||
if (!alloc_buffer(G1HeapRegionAttr::Old, 0)->contains(obj_start)) { | ||
// Out of PLAB allocation, BOT already updated. | ||
return; | ||
} | ||
|
||
// Update the BOT. The threshold also gets updated to the next threshold by this call. | ||
_bot_plab_region->update_bot_crossing_threshold(&_bot_plab_threshold, obj_start, obj_end); | ||
} | ||
|
||
#endif // SHARE_GC_G1_G1ALLOCATOR_INLINE_HPP |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -498,6 +498,9 @@ oop G1ParScanThreadState::do_copy_to_survivor_space(G1HeapRegionAttr const regio | |
obj->incr_age(); | ||
} | ||
_age_table.add(age, word_sz); | ||
} else { | ||
assert(dest_attr.is_old(), "Only update bot for allocations in old"); | ||
_plab_allocator->update_bot_for_object(obj_ptr, word_sz); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe it would be good if the
Then this struct could be passed in here again instead of the code for Or just skip the I think explicitly carrying this information around would be much much cleaner to understand than trying to reconstruct that information later in `update_bot_for_object´ via
|
||
} | ||
|
||
// Most objects are not arrays, so do one array check rather than | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May I ask why do we need to update for the filler objects? Because we are not scanning them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question. You are correct in that we never need to scan these parts because of dirty cards, but during remembered set rebuilding (see:
G1RebuildRemSetHeapRegionClosure::rebuild_rem_set_in_region(...)
) we include the whole region when looking for references to other heap regions.There might be some good way to avoid scanning those parts during rebuild, but such investigation is out of scope for this PR.
Thanks for reviewing 😄