-
Notifications
You must be signed in to change notification settings - Fork 36
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
8338534: GenShen: Handle alloc failure differently when immediate garbage is pending #479
base: master
Are you sure you want to change the base?
Changes from 28 commits
7bb1d38
8bc4367
99cce53
11b26bb
941d8aa
39c5885
28a382b
a43675a
d881300
c2cb1b7
141fec1
bac08f0
84f27d7
118f5b1
5312029
56567b0
25ee3f5
c076aa3
ff99de7
b8b4e42
1c26ae0
634ef66
4198b75
408f789
9e9c54c
8461939
02fd5c1
5c0bb7d
f11a3c8
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 |
---|---|---|
|
@@ -53,6 +53,10 @@ size_t ShenandoahController::get_gc_id() { | |
return Atomic::load(&_gc_id); | ||
} | ||
|
||
void ShenandoahController::anticipate_immediate_garbage(size_t anticipated_immediate_garbage) { | ||
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. Suggested rename see further above. |
||
Atomic::store(&_anticipated_immediate_garbage, anticipated_immediate_garbage); | ||
} | ||
|
||
void ShenandoahController::handle_alloc_failure(ShenandoahAllocRequest& req, bool block) { | ||
ShenandoahHeap* heap = ShenandoahHeap::heap(); | ||
|
||
|
@@ -65,11 +69,12 @@ void ShenandoahController::handle_alloc_failure(ShenandoahAllocRequest& req, boo | |
req.type_string(), | ||
byte_size_in_proper_unit(req.size() * HeapWordSize), proper_unit_for_byte_size(req.size() * HeapWordSize)); | ||
|
||
// Now that alloc failure GC is scheduled, we can abort everything else | ||
heap->cancel_gc(GCCause::_allocation_failure); | ||
if (Atomic::load(&_anticipated_immediate_garbage) < req.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. To make sure I understand... here we are saying that if final mark anticipates this much immediate garbage (computed when it rebuilt the freeset after choosing the collection set), then we aren't going to cancel the GC if this particular request could be satisfied. Instead we will block as though the gc has already been cancelled. This thread will be notified when concurrent cleanup completes. |
||
// Now that alloc failure GC is scheduled, we can abort everything else | ||
heap->cancel_gc(GCCause::_allocation_failure); | ||
} | ||
} | ||
|
||
|
||
if (block) { | ||
MonitorLocker ml(&_alloc_failure_waiters_lock); | ||
while (is_alloc_failure_gc()) { | ||
|
@@ -92,9 +97,11 @@ void ShenandoahController::handle_alloc_failure_evac(size_t words) { | |
heap->cancel_gc(GCCause::_shenandoah_allocation_failure_evac); | ||
} | ||
|
||
void ShenandoahController::notify_alloc_failure_waiters() { | ||
_alloc_failure_gc.unset(); | ||
_humongous_alloc_failure_gc.unset(); | ||
void ShenandoahController::notify_alloc_failure_waiters(bool clear_alloc_failure) { | ||
if (clear_alloc_failure) { | ||
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. Why would we not clear the alloc failure? This seems like it would confuse the control thread. Isn't this going to have the control thread attempt to notify alloc failure waiters again when the cycle is finished? |
||
_alloc_failure_gc.unset(); | ||
_humongous_alloc_failure_gc.unset(); | ||
} | ||
Comment on lines
+101
to
+104
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. For good hygiene, I'd move the variable value changes into the monitor which is held when waiting or notifying. I realize this doesn't matter for correctness, but makes debugging easier. Further, if you protect the updates and reads of the variables with the lock, you don't need to do the extra atomic ops. You'd need to examine all sets/gets and waits/notifys to make sure this works, but I am guessing it will, and it'll also improve performance. However, that can be done in a separate effort, if you prefer, for which I'm happy to file a separate ticket for that investigation/change. 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 realize now that this idiom is quite pervasive in Shenandoah code, so just fixing this instance of it doesn't accomplish much at this time. I am not convinced it's a good idiom. I'll investigate this separately. I vaguely recall a discussion along these lines in an older PR. I'll file a separate ticket for this; you can ignore this remark for the purposes of this PR. |
||
MonitorLocker ml(&_alloc_failure_waiters_lock); | ||
ml.notify_all(); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -43,6 +43,8 @@ class ShenandoahController: public ConcurrentGCThread { | |
shenandoah_padding(1); | ||
volatile size_t _gc_id; | ||
shenandoah_padding(2); | ||
volatile size_t _anticipated_immediate_garbage; | ||
shenandoah_padding(3); | ||
|
||
protected: | ||
ShenandoahSharedFlag _alloc_failure_gc; | ||
|
@@ -71,6 +73,8 @@ class ShenandoahController: public ConcurrentGCThread { | |
// until another cycle runs and clears the alloc failure gc flag. | ||
void handle_alloc_failure(ShenandoahAllocRequest& req, bool block); | ||
|
||
void anticipate_immediate_garbage(size_t anticipated_immediate_garbage_words); | ||
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. a 1-line documentation comment on the role of the field (and that the method sets it -- why not simply call it |
||
|
||
// Invoked for allocation failures during evacuation. This cancels | ||
// the collection cycle without blocking. | ||
void handle_alloc_failure_evac(size_t words); | ||
|
@@ -79,7 +83,7 @@ class ShenandoahController: public ConcurrentGCThread { | |
bool try_set_alloc_failure_gc(bool is_humongous); | ||
|
||
// Notify threads waiting for GC to complete. | ||
void notify_alloc_failure_waiters(); | ||
void notify_alloc_failure_waiters(bool clear_alloc_failure = true); | ||
|
||
// True if allocation failure flag has been set. | ||
bool is_alloc_failure_gc(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -111,6 +111,9 @@ ShenandoahRegionPartitions::ShenandoahRegionPartitions(size_t max_regions, Shena | |
} | ||
|
||
inline bool ShenandoahFreeSet::can_allocate_from(ShenandoahHeapRegion *r) const { | ||
// This test for trash regions is conservative. Strictly, we only need to assure that concurrent weak reference processing | ||
// is not under way. That finishes long before concurrent weak root processing. It is ok to be conservative. At the | ||
// end of weak reference processing, we recycle trashed regions en masse. | ||
return r->is_empty() || (r->is_trash() && !_heap->is_concurrent_weak_root_in_progress()); | ||
} | ||
|
||
|
@@ -1230,13 +1233,17 @@ HeapWord* ShenandoahFreeSet::allocate_contiguous(ShenandoahAllocRequest& req) { | |
return _heap->get_region(beg)->bottom(); | ||
} | ||
|
||
void ShenandoahFreeSet::try_recycle_trashed(ShenandoahHeapRegion* r) { | ||
bool ShenandoahFreeSet::try_recycle_trashed(ShenandoahHeapRegion* r) { | ||
bool result = false; | ||
if (r->is_trash()) { | ||
r->recycle(); | ||
result = true; | ||
} | ||
return true; | ||
} | ||
Comment on lines
+1236
to
1243
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. If I understood your intent, I think this has a bug because it always returns true here. I believe you just wanted:
|
||
|
||
void ShenandoahFreeSet::recycle_trash() { | ||
bool ShenandoahFreeSet::recycle_trash() { | ||
bool result = false; | ||
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'd take the opportunity to do some counting verification here.
Read on ... |
||
// lock is not reentrable, check we don't have it | ||
shenandoah_assert_not_heaplocked(); | ||
|
||
|
@@ -1256,9 +1263,13 @@ void ShenandoahFreeSet::recycle_trash() { | |
ShenandoahHeapLocker locker(_heap->lock()); | ||
const jlong deadline = os::javaTimeNanos() + deadline_ns; | ||
while (idx < count && os::javaTimeNanos() < deadline) { | ||
try_recycle_trashed(_trash_regions[idx++]); | ||
if (try_recycle_trashed(_trash_regions[idx++])) { | ||
result = true; | ||
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. ...
... |
||
} | ||
} | ||
} | ||
_heap->control_thread()->anticipate_immediate_garbage((size_t) 0); | ||
return result; | ||
Comment on lines
+1271
to
+1272
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. ...
with
Is this an intended invariant? I think it is, but don't understand enough of the details to be certain. |
||
} | ||
|
||
void ShenandoahFreeSet::flip_to_old_gc(ShenandoahHeapRegion* r) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -753,6 +753,9 @@ void ShenandoahGeneration::prepare_regions_and_collection_set(bool concurrent) { | |
// We are preparing for evacuation. At this time, we ignore cset region tallies. | ||
size_t first_old, last_old, num_old; | ||
heap->free_set()->prepare_to_rebuild(young_cset_regions, old_cset_regions, first_old, last_old, num_old); | ||
size_t anticipated_immediate_garbage = (old_cset_regions + young_cset_regions) * ShenandoahHeapRegion::region_size_words(); | ||
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 makes it sound like old_cset_regions & young_cset_regions hold all regions that will be part of In summary, firstly, I feel some of these methods are in need of tighter header file documentation of post-conditions that callers are relying on and, secondly, I feel given the extremely fat APIs (lots of reference variables that are modified by these methods) that some amount of refactoring is needed in the longer term. The refactoring should be a separate effort, but in the shorter term I think the API/spec documentation of 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. The names |
||
heap->control_thread()->anticipate_immediate_garbage(anticipated_immediate_garbage); | ||
|
||
// Free set construction uses reserve quantities, because they are known to be valid here | ||
heap->free_set()->finish_rebuild(young_cset_regions, old_cset_regions, num_old, true); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2436,9 +2436,9 @@ | |
ShenandoahPhaseTimings::final_update_refs_update_region_states : | ||
ShenandoahPhaseTimings::degen_gc_final_update_refs_update_region_states); | ||
|
||
final_update_refs_update_region_states(); | ||
final_update_refs_update_region_states(); | ||
|
||
assert_pinned_region_status(); | ||
assert_pinned_region_status(); | ||
} | ||
|
||
{ | ||
|
@@ -2447,7 +2447,7 @@ | |
ShenandoahPhaseTimings::degen_gc_final_update_refs_trash_cset); | ||
trash_cset_regions(); | ||
} | ||
} | ||
|
||
void ShenandoahHeap::final_update_refs_update_region_states() { | ||
ShenandoahSynchronizePinnedRegionStates cl; | ||
|
@@ -2462,6 +2462,9 @@ | |
size_t young_cset_regions, old_cset_regions; | ||
size_t first_old_region, last_old_region, old_region_count; | ||
_free_set->prepare_to_rebuild(young_cset_regions, old_cset_regions, first_old_region, last_old_region, old_region_count); | ||
size_t anticipated_immediate_garbage = (old_cset_regions + young_cset_regions) * ShenandoahHeapRegion::region_size_words(); | ||
control_thread()->anticipate_immediate_garbage(anticipated_immediate_garbage); | ||
|
||
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 the line that has two whitespaces, vide the jcheck whitespace error above. |
||
// If there are no old regions, first_old_region will be greater than last_old_region | ||
assert((first_old_region > last_old_region) || | ||
((last_old_region + 1 - first_old_region >= old_region_count) && | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -474,6 +474,9 @@ void ShenandoahOldGeneration::prepare_regions_and_collection_set(bool concurrent | |
size_t cset_young_regions, cset_old_regions; | ||
size_t first_old, last_old, num_old; | ||
heap->free_set()->prepare_to_rebuild(cset_young_regions, cset_old_regions, first_old, last_old, num_old); | ||
size_t anticipated_immediate_garbage = (cset_young_regions + cset_old_regions) * ShenandoahHeapRegion::region_size_words(); | ||
heap->control_thread()->anticipate_immediate_garbage(anticipated_immediate_garbage); | ||
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. suggest |
||
|
||
// This is just old-gen completion. No future budgeting required here. The only reason to rebuild the freeset here | ||
// is in case there was any immediate old garbage identified. | ||
heap->free_set()->finish_rebuild(cset_young_regions, cset_old_regions, num_old); | ||
|
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.
Can you motivate this notification? As far as I can tell, all waiters will react to the notification by waking up, finding that the variables are still set, and consequently go back to wait.
I am sure I am missing something here, or you didn't make an intended change to allow waiters to retry allocation after waking up and go back to sleep if they didn't succeed?
A documentation comment would definitely help cross the t's and dot the i's for the reader.