< prev index next >

src/hotspot/share/gc/g1/vm_operations_g1.cpp

Print this page
rev 48467 : 8137099: G1 needs to "upgrade" GC within the safepoint if it can't allocate during that safepoint to avoid OoME
Summary: During a minor GC, if memory allocation fails, start a full GC within the same VM operation in the same safepoint. This avoids a race where the GC locker can prevent the full GC from occurring, and a premature OoME.
Reviewed-by:
Contributed-by: thomas.schatzl@oracle.com, axel.siebenborn@sap.com
rev 48469 : imported patch 8137099-sjohanns-messages
rev 48470 : [mq]: 8137099-erikd-review
rev 48471 : [mq]: 8137099-erikd-review2

*** 1,7 **** /* ! * Copyright (c) 2001, 2016, 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. --- 1,7 ---- /* ! * Copyright (c) 2001, 2018, 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.
*** 31,80 **** #include "gc/shared/gcTimer.hpp" #include "gc/shared/gcTraceTime.inline.hpp" #include "gc/shared/isGCActiveMark.hpp" #include "runtime/interfaceSupport.hpp" - VM_G1CollectForAllocation::VM_G1CollectForAllocation(uint gc_count_before, - size_t word_size) - : VM_G1OperationWithAllocRequest(gc_count_before, word_size, - GCCause::_allocation_failure) { - guarantee(word_size != 0, "An allocation should always be requested with this operation."); - } - - void VM_G1CollectForAllocation::doit() { - G1CollectedHeap* g1h = G1CollectedHeap::heap(); - GCCauseSetter x(g1h, _gc_cause); - - _result = g1h->satisfy_failed_allocation(_word_size, allocation_context(), &_pause_succeeded); - assert(_result == NULL || _pause_succeeded, - "if we get back a result, the pause should have succeeded"); - } - void VM_G1CollectFull::doit() { G1CollectedHeap* g1h = G1CollectedHeap::heap(); GCCauseSetter x(g1h, _gc_cause); g1h->do_full_collection(false /* clear_all_soft_refs */); } ! VM_G1IncCollectionPause::VM_G1IncCollectionPause(uint gc_count_before, ! size_t word_size, bool should_initiate_conc_mark, double target_pause_time_ms, ! GCCause::Cause gc_cause) ! : VM_G1OperationWithAllocRequest(gc_count_before, word_size, gc_cause), _should_initiate_conc_mark(should_initiate_conc_mark), _target_pause_time_ms(target_pause_time_ms), _should_retry_gc(false), _old_marking_cycles_completed_before(0) { guarantee(target_pause_time_ms > 0.0, "target_pause_time_ms = %1.6lf should be positive", target_pause_time_ms); _gc_cause = gc_cause; } ! bool VM_G1IncCollectionPause::doit_prologue() { ! bool res = VM_G1OperationWithAllocRequest::doit_prologue(); if (!res) { if (_should_initiate_conc_mark) { // The prologue can fail for a couple of reasons. The first is that another GC // got scheduled and prevented the scheduling of the initial mark GC. The // second is that the GC locker may be active and the heap can't be expanded. --- 31,67 ---- #include "gc/shared/gcTimer.hpp" #include "gc/shared/gcTraceTime.inline.hpp" #include "gc/shared/isGCActiveMark.hpp" #include "runtime/interfaceSupport.hpp" void VM_G1CollectFull::doit() { G1CollectedHeap* g1h = G1CollectedHeap::heap(); GCCauseSetter x(g1h, _gc_cause); g1h->do_full_collection(false /* clear_all_soft_refs */); } ! VM_G1CollectForAllocation::VM_G1CollectForAllocation(size_t word_size, ! uint gc_count_before, ! GCCause::Cause gc_cause, bool should_initiate_conc_mark, double target_pause_time_ms, ! AllocationContext_t allocation_context) ! : VM_CollectForAllocation(word_size, gc_count_before, gc_cause), ! _pause_succeeded(false), ! _allocation_context(allocation_context), _should_initiate_conc_mark(should_initiate_conc_mark), _target_pause_time_ms(target_pause_time_ms), _should_retry_gc(false), _old_marking_cycles_completed_before(0) { guarantee(target_pause_time_ms > 0.0, "target_pause_time_ms = %1.6lf should be positive", target_pause_time_ms); _gc_cause = gc_cause; } ! bool VM_G1CollectForAllocation::doit_prologue() { ! bool res = VM_CollectForAllocation::doit_prologue(); if (!res) { if (_should_initiate_conc_mark) { // The prologue can fail for a couple of reasons. The first is that another GC // got scheduled and prevented the scheduling of the initial mark GC. The // second is that the GC locker may be active and the heap can't be expanded.
*** 85,103 **** } } return res; } ! void VM_G1IncCollectionPause::doit() { G1CollectedHeap* g1h = G1CollectedHeap::heap(); assert(!_should_initiate_conc_mark || g1h->should_do_concurrent_full_gc(_gc_cause), "only a GC locker, a System.gc(), stats update, whitebox, or a hum allocation induced GC should start a cycle"); if (_word_size > 0) { // An allocation has been requested. So, try to do that first. _result = g1h->attempt_allocation_at_safepoint(_word_size, ! allocation_context(), false /* expect_null_cur_alloc_region */); if (_result != NULL) { // If we can successfully allocate before we actually do the // pause then we will consider this pause successful. _pause_succeeded = true; --- 72,90 ---- } } return res; } ! void VM_G1CollectForAllocation::doit() { G1CollectedHeap* g1h = G1CollectedHeap::heap(); assert(!_should_initiate_conc_mark || g1h->should_do_concurrent_full_gc(_gc_cause), "only a GC locker, a System.gc(), stats update, whitebox, or a hum allocation induced GC should start a cycle"); if (_word_size > 0) { // An allocation has been requested. So, try to do that first. _result = g1h->attempt_allocation_at_safepoint(_word_size, ! _allocation_context, false /* expect_null_cur_alloc_region */); if (_result != NULL) { // If we can successfully allocate before we actually do the // pause then we will consider this pause successful. _pause_succeeded = true;
*** 142,172 **** } return; } } ! _pause_succeeded = ! g1h->do_collection_pause_at_safepoint(_target_pause_time_ms); ! if (_pause_succeeded && _word_size > 0) { ! // An allocation had been requested. ! _result = g1h->attempt_allocation_at_safepoint(_word_size, ! allocation_context(), ! true /* expect_null_cur_alloc_region */); } else { assert(_result == NULL, "invariant"); ! if (!_pause_succeeded) { ! // Another possible reason reason for the pause to not be successful ! // is that, again, the GC locker is active (and has become active ! // since the prologue was executed). In this case we should retry ! // the pause after waiting for the GC locker to become inactive. _should_retry_gc = true; } - } } ! void VM_G1IncCollectionPause::doit_epilogue() { ! VM_G1OperationWithAllocRequest::doit_epilogue(); // If the pause was initiated by a System.gc() and // +ExplicitGCInvokesConcurrent, we have to wait here for the cycle // that just started (or maybe one that was already in progress) to // finish. --- 129,166 ---- } return; } } ! // Try a partial collection of some kind. ! _pause_succeeded = g1h->do_collection_pause_at_safepoint(_target_pause_time_ms); ! ! if (_pause_succeeded) { ! if (_word_size > 0) { ! // An allocation had been requested. Do it, eventually trying a stronger ! // kind of GC. ! _result = g1h->satisfy_failed_allocation(_word_size, _allocation_context, &_pause_succeeded); ! } else if (!g1h->has_regions_left_for_allocation()) { ! // There has been a request to perform a GC to free some space. We have no ! // information on how much memory has been asked for. In case there are ! // absolutely no regions left to allocate into, do a maximally compacting full GC. ! log_info(gc, ergo)("Attempting maximally compacting collection"); ! _pause_succeeded = g1h->do_full_collection(false, /* explicit gc */ ! true /* clear_all_soft_refs */); ! } ! guarantee(_pause_succeeded, "Elevated collections during the safepoint must always succeed."); } else { assert(_result == NULL, "invariant"); ! // The only reason for the pause to not be successful is that, the GC locker is ! // active (or has become active since the prologue was executed). In this case ! // we should retry the pause after waiting for the GC locker to become inactive. _should_retry_gc = true; } } ! void VM_G1CollectForAllocation::doit_epilogue() { ! VM_CollectForAllocation::doit_epilogue(); // If the pause was initiated by a System.gc() and // +ExplicitGCInvokesConcurrent, we have to wait here for the cycle // that just started (or maybe one that was already in progress) to // finish.
< prev index next >