--- a/hotspot/src/share/vm/gc/g1/g1ParScanThreadState.cpp Thu Aug 20 10:27:04 2015 +0200
+++ b/hotspot/src/share/vm/gc/g1/g1ParScanThreadState.cpp Wed Aug 19 13:47:40 2015 +0200
@@ -41,7 +41,9 @@
_term_attempts(0),
_tenuring_threshold(g1h->g1_policy()->tenuring_threshold()),
_age_table(false), _scanner(g1h, rp),
- _strong_roots_time(0), _term_time(0) {
+ _strong_roots_time(0), _term_time(0),
+ _old_gen_is_full(false)
+{
_scanner.set_par_scan_thread_state(this);
// we allocate G1YoungSurvRateNumRegions plus one entries, since
// we "sacrifice" entry 0 to keep track of surviving bytes for
@@ -152,26 +154,38 @@
HeapWord* G1ParScanThreadState::allocate_in_next_plab(InCSetState const state,
InCSetState* dest,
size_t word_sz,
- AllocationContext_t const context) {
+ AllocationContext_t const context,
+ bool previous_plab_refill_failed) {
assert(state.is_in_cset_or_humongous(), err_msg("Unexpected state: " CSETSTATE_FORMAT, state.value()));
assert(dest->is_in_cset_or_humongous(), err_msg("Unexpected dest: " CSETSTATE_FORMAT, dest->value()));
// Right now we only have two types of regions (young / old) so
// let's keep the logic here simple. We can generalize it when necessary.
if (dest->is_young()) {
+ bool plab_refill_in_old_failed = false;
HeapWord* const obj_ptr = _plab_allocator->allocate(InCSetState::Old,
word_sz,
- context);
- if (obj_ptr == NULL) {
- return NULL;
- }
+ context,
+ &plab_refill_in_old_failed);
// Make sure that we won't attempt to copy any other objects out
// of a survivor region (given that apparently we cannot allocate
- // any new ones) to avoid coming into this slow path.
- _tenuring_threshold = 0;
- dest->set_old();
+ // any new ones) to avoid coming into this slow path again and again.
+ // Only consider failed PLAB refill here: failed inline allocations are
+ // typically large, so not indicative of remaining space.
+ if (previous_plab_refill_failed) {
+ _tenuring_threshold = 0;
+ }
+
+ if (obj_ptr != NULL) {
+ dest->set_old();
+ } else {
+ // We just failed to allocate in old gen. The same idea as explained above
+ // for making survivor gen unavailable for allocation applies for old gen.
+ _old_gen_is_full = plab_refill_in_old_failed;
+ }
return obj_ptr;
} else {
+ _old_gen_is_full = previous_plab_refill_failed;
assert(dest->is_old(), err_msg("Unexpected dest: " CSETSTATE_FORMAT, dest->value()));
// no other space to try.
return NULL;
@@ -202,14 +216,20 @@
uint age = 0;
InCSetState dest_state = next_state(state, old_mark, age);
+ // The second clause is to prevent premature evacuation failure in case there
+ // is still space in survivor, but old gen is full.
+ if (_old_gen_is_full && dest_state.is_old()) {
+ return handle_evacuation_failure_par(old, old_mark);
+ }
HeapWord* obj_ptr = _plab_allocator->plab_allocate(dest_state, word_sz, context);
// PLAB allocations should succeed most of the time, so we'll
// normally check against NULL once and that's it.
if (obj_ptr == NULL) {
- obj_ptr = _plab_allocator->allocate_direct_or_new_plab(dest_state, word_sz, context);
+ bool plab_refill_failed = false;
+ obj_ptr = _plab_allocator->allocate_direct_or_new_plab(dest_state, word_sz, context, &plab_refill_failed);
if (obj_ptr == NULL) {
- obj_ptr = allocate_in_next_plab(state, &dest_state, word_sz, context);
+ obj_ptr = allocate_in_next_plab(state, &dest_state, word_sz, context, plab_refill_failed);
if (obj_ptr == NULL) {
// This will either forward-to-self, or detect that someone else has
// installed a forwarding pointer.