diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index a6233fa3c26..25d7ed13ab0 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -70,7 +70,7 @@ #include "gc/g1/g1UncommitRegionTask.hpp" #include "gc/g1/g1VMOperations.hpp" #include "gc/g1/g1YoungCollector.hpp" -#include "gc/g1/g1YoungGCEvacFailureInjector.hpp" +#include "gc/g1/g1YoungGCAllocationFailureInjector.hpp" #include "gc/g1/heapRegion.inline.hpp" #include "gc/g1/heapRegionRemSet.inline.hpp" #include "gc/g1/heapRegionSet.inline.hpp" @@ -1144,7 +1144,7 @@ G1CollectedHeap::G1CollectedHeap() : _numa(G1NUMA::create()), _hrm(), _allocator(nullptr), - _evac_failure_injector(), + _allocation_failure_injector(), _verifier(nullptr), _summary_bytes_used(0), _bytes_used_during_gc(0), @@ -1432,7 +1432,7 @@ jint G1CollectedHeap::initialize() { _collection_set.initialize(max_reserved_regions()); - evac_failure_injector()->reset(); + allocation_failure_injector()->reset(); CPUTimeCounters::create_counter(CPUTimeGroups::CPUTimeType::gc_parallel_workers); CPUTimeCounters::create_counter(CPUTimeGroups::CPUTimeType::gc_conc_mark); @@ -3002,9 +3002,6 @@ void G1CollectedHeap::unregister_nmethod(nmethod* nm) { void G1CollectedHeap::update_used_after_gc(bool evacuation_failed) { if (evacuation_failed) { - // Reset the G1EvacuationFailureALot counters and flags - evac_failure_injector()->reset(); - set_used(recalculate_used()); } else { // The "used" of the collection set have already been subtracted diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index 9ae55501066..1d25ea278de 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -44,7 +44,7 @@ #include "gc/g1/g1MonotonicArenaFreePool.hpp" #include "gc/g1/g1NUMA.hpp" #include "gc/g1/g1SurvivorRegions.hpp" -#include "gc/g1/g1YoungGCEvacFailureInjector.hpp" +#include "gc/g1/g1YoungGCAllocationFailureInjector.hpp" #include "gc/g1/heapRegionManager.hpp" #include "gc/g1/heapRegionSet.hpp" #include "gc/shared/barrierSet.hpp" @@ -220,7 +220,7 @@ private: // Manages all allocations with regions except humongous object allocations. G1Allocator* _allocator; - G1YoungGCEvacFailureInjector _evac_failure_injector; + G1YoungGCAllocationFailureInjector _allocation_failure_injector; // Manages all heap verification. G1HeapVerifier* _verifier; @@ -550,7 +550,7 @@ public: return _allocator; } - G1YoungGCEvacFailureInjector* evac_failure_injector() { return &_evac_failure_injector; } + G1YoungGCAllocationFailureInjector* allocation_failure_injector() { return &_allocation_failure_injector; } G1HeapVerifier* verifier() { return _verifier; diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp index d011516fba1..8d7ee0b1dfd 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp @@ -32,7 +32,7 @@ #include "gc/g1/g1RootClosures.hpp" #include "gc/g1/g1StringDedup.hpp" #include "gc/g1/g1Trace.hpp" -#include "gc/g1/g1YoungGCEvacFailureInjector.inline.hpp" +#include "gc/g1/g1YoungGCAllocationFailureInjector.inline.hpp" #include "gc/shared/continuationGCSupport.inline.hpp" #include "gc/shared/partialArrayTaskStepper.inline.hpp" #include "gc/shared/preservedMarks.inline.hpp" @@ -85,7 +85,7 @@ G1ParScanThreadState::G1ParScanThreadState(G1CollectedHeap* g1h, _max_num_optional_regions(collection_set->optional_region_length()), _numa(g1h->numa()), _obj_alloc_stat(nullptr), - EVAC_FAILURE_INJECTOR_ONLY(_evac_failure_inject_counter(0) COMMA) + ALLOCATION_FAILURE_INJECTOR_ONLY(_allocation_failure_inject_counter(0) COMMA) _preserved_marks(preserved_marks), _evacuation_failed_info(), _evac_failure_regions(evac_failure_regions), @@ -427,9 +427,9 @@ HeapWord* G1ParScanThreadState::allocate_copy_slow(G1HeapRegionAttr* dest_attr, return obj_ptr; } -#if EVAC_FAILURE_INJECTOR +#if ALLOCATION_FAILURE_INJECTOR bool G1ParScanThreadState::inject_allocation_failure(uint region_idx) { - return _g1h->evac_failure_injector()->evacuation_should_fail(_evac_failure_inject_counter, region_idx); + return _g1h->allocation_failure_injector()->allocation_should_fail(_allocation_failure_inject_counter, region_idx); } #endif diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp index 5347b6341f8..4f72e61f82b 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp @@ -28,7 +28,7 @@ #include "gc/g1/g1CollectedHeap.hpp" #include "gc/g1/g1RedirtyCardsQueue.hpp" #include "gc/g1/g1OopClosures.hpp" -#include "gc/g1/g1YoungGCEvacFailureInjector.hpp" +#include "gc/g1/g1YoungGCAllocationFailureInjector.hpp" #include "gc/g1/g1_globals.hpp" #include "gc/shared/ageTable.hpp" #include "gc/shared/copyFailedInfo.hpp" @@ -104,7 +104,7 @@ class G1ParScanThreadState : public CHeapObj { size_t* _obj_alloc_stat; // Per-thread evacuation failure data structures. - EVAC_FAILURE_INJECTOR_ONLY(size_t _evac_failure_inject_counter;) + ALLOCATION_FAILURE_INJECTOR_ONLY(size_t _allocation_failure_inject_counter;) PreservedMarks* _preserved_marks; EvacuationFailedInfo _evacuation_failed_info; @@ -120,7 +120,7 @@ class G1ParScanThreadState : public CHeapObj { // Enqueue the card of p into the (evacuation failed) region. template void enqueue_card_into_evac_fail_region(T* p, oop obj); - bool inject_allocation_failure(uint region_idx) EVAC_FAILURE_INJECTOR_RETURN_( return false; ); + bool inject_allocation_failure(uint region_idx) ALLOCATION_FAILURE_INJECTOR_RETURN_( return false; ); public: G1ParScanThreadState(G1CollectedHeap* g1h, diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.cpp b/src/hotspot/share/gc/g1/g1YoungCollector.cpp index 81d46aa3979..ec32f8b589b 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.cpp @@ -34,7 +34,6 @@ #include "gc/g1/g1CollectorState.hpp" #include "gc/g1/g1ConcurrentMark.hpp" #include "gc/g1/g1GCPhaseTimes.hpp" -#include "gc/g1/g1YoungGCEvacFailureInjector.hpp" #include "gc/g1/g1EvacFailureRegions.inline.hpp" #include "gc/g1/g1EvacInfo.hpp" #include "gc/g1/g1HRPrinter.hpp" @@ -46,6 +45,7 @@ #include "gc/g1/g1RootProcessor.hpp" #include "gc/g1/g1Trace.hpp" #include "gc/g1/g1YoungCollector.hpp" +#include "gc/g1/g1YoungGCAllocationFailureInjector.hpp" #include "gc/g1/g1YoungGCPostEvacuateTasks.hpp" #include "gc/g1/g1YoungGCPreEvacuateTasks.hpp" #include "gc/g1/g1_globals.hpp" @@ -243,8 +243,8 @@ WorkerThreads* G1YoungCollector::workers() const { return _g1h->workers(); } -G1YoungGCEvacFailureInjector* G1YoungCollector::evac_failure_injector() const { - return _g1h->evac_failure_injector(); +G1YoungGCAllocationFailureInjector* G1YoungCollector::allocation_failure_injector() const { + return _g1h->allocation_failure_injector(); } @@ -534,7 +534,7 @@ void G1YoungCollector::pre_evacuate_collection_set(G1EvacInfo* evacuation_info) DerivedPointerTable::clear(); #endif - evac_failure_injector()->arm_if_needed(); + allocation_failure_injector()->arm_if_needed(); } class G1ParEvacuateFollowersClosure : public VoidClosure { diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.hpp b/src/hotspot/share/gc/g1/g1YoungCollector.hpp index 506021a1bef..e7f3c51b65c 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.hpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.hpp @@ -26,7 +26,7 @@ #define SHARE_GC_G1_G1YOUNGCOLLECTOR_HPP #include "gc/g1/g1EvacFailureRegions.hpp" -#include "gc/g1/g1YoungGCEvacFailureInjector.hpp" +#include "gc/g1/g1YoungGCAllocationFailureInjector.hpp" #include "gc/shared/gcCause.hpp" #include "gc/shared/taskqueue.hpp" @@ -49,7 +49,7 @@ class G1Policy; class G1RedirtyCardsQueueSet; class G1RemSet; class G1SurvivorRegions; -class G1YoungGCEvacFailureInjector; +class G1YoungGCAllocationFailureInjector; class STWGCTimer; class WorkerThreads; @@ -78,7 +78,7 @@ class G1YoungCollector { G1SurvivorRegions* survivor_regions() const; ReferenceProcessor* ref_processor_stw() const; WorkerThreads* workers() const; - G1YoungGCEvacFailureInjector* evac_failure_injector() const; + G1YoungGCAllocationFailureInjector* allocation_failure_injector() const; GCCause::Cause _gc_cause; diff --git a/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp b/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp new file mode 100644 index 00000000000..60b7e0872f2 --- /dev/null +++ b/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2021, 2023, 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" + +#include "gc/g1/g1CollectedHeap.inline.hpp" +#include "gc/g1/g1YoungGCAllocationFailureInjector.inline.hpp" +#include "gc/g1/g1_globals.hpp" + +#if ALLOCATION_FAILURE_INJECTOR + +class SelectAllocationFailureRegionClosure : public HeapRegionClosure { + CHeapBitMap& _allocation_failure_regions; + size_t _allocation_failure_regions_num; + +public: + SelectAllocationFailureRegionClosure(CHeapBitMap& allocation_failure_regions, size_t cset_length) : + _allocation_failure_regions(allocation_failure_regions), + _allocation_failure_regions_num(cset_length * G1GCAllocationFailureALotCSetPercent / 100) { } + + bool do_heap_region(HeapRegion* r) override { + assert(r->in_collection_set(), "must be"); + if (_allocation_failure_regions_num > 0) { + _allocation_failure_regions.set_bit(r->hrm_index()); + --_allocation_failure_regions_num; + } + return _allocation_failure_regions_num == 0; + } +}; + +G1YoungGCAllocationFailureInjector::G1YoungGCAllocationFailureInjector() + : _inject_allocation_failure_for_current_gc(), + _last_collection_with_allocation_failure(), + _allocation_failure_regions(mtGC) {} + +void G1YoungGCAllocationFailureInjector::select_allocation_failure_regions() { + G1CollectedHeap* g1h = G1CollectedHeap::heap(); + _allocation_failure_regions.reinitialize(g1h->max_reserved_regions()); + SelectAllocationFailureRegionClosure closure(_allocation_failure_regions, g1h->collection_set()->cur_length()); + g1h->collection_set_iterate_all(&closure); +} + +bool G1YoungGCAllocationFailureInjector::arm_if_needed_for_gc_type(bool for_young_only_phase, + bool during_concurrent_start, + bool mark_or_rebuild_in_progress) { + bool res = false; + if (mark_or_rebuild_in_progress) { + res |= G1GCAllocationFailureALotDuringConcMark; + } + if (during_concurrent_start) { + res |= G1GCAllocationFailureALotDuringConcurrentStart; + } + if (for_young_only_phase) { + res |= G1GCAllocationFailureALotDuringYoungGC; + } else { + // GCs are mixed + res |= G1GCAllocationFailureALotDuringMixedGC; + } + return res; +} + +void G1YoungGCAllocationFailureInjector::arm_if_needed() { + if (G1GCAllocationFailureALot) { + G1CollectedHeap* g1h = G1CollectedHeap::heap(); + // Check if we have gone over the interval. + const size_t gc_num = g1h->total_collections(); + const size_t elapsed_gcs = gc_num - _last_collection_with_allocation_failure; + + _inject_allocation_failure_for_current_gc = (elapsed_gcs >= G1GCAllocationFailureALotInterval); + + // Now check if evacuation failure injection should be enabled for the current GC. + G1CollectorState* collector_state = g1h->collector_state(); + const bool in_young_only_phase = collector_state->in_young_only_phase(); + const bool in_concurrent_start_gc = collector_state->in_concurrent_start_gc(); + const bool mark_or_rebuild_in_progress = collector_state->mark_or_rebuild_in_progress(); + + _inject_allocation_failure_for_current_gc &= + arm_if_needed_for_gc_type(in_young_only_phase, + in_concurrent_start_gc, + mark_or_rebuild_in_progress); + + if (_inject_allocation_failure_for_current_gc) { + select_allocation_failure_regions(); + } + } +} + +void G1YoungGCAllocationFailureInjector::reset() { + _last_collection_with_allocation_failure = G1CollectedHeap::heap()->total_collections(); + _inject_allocation_failure_for_current_gc = false; +} + +#endif // #if ALLOCATION_FAILURE_INJECTOR diff --git a/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.hpp b/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.hpp new file mode 100644 index 00000000000..fb7e8cd3ad3 --- /dev/null +++ b/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.hpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2021, 2023, 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_GC_G1_G1YOUNGGCALLOCATIONFAILUREINJECTOR_HPP +#define SHARE_GC_G1_G1YOUNGGCALLOCATIONFAILUREINJECTOR_HPP + +#include "gc/g1/g1_globals.hpp" +#include "memory/allStatic.hpp" +#include "utilities/globalDefinitions.hpp" + +#if ALLOCATION_FAILURE_INJECTOR +#define ALLOCATION_FAILURE_INJECTOR_RETURN +#define ALLOCATION_FAILURE_INJECTOR_RETURN_(code) +#define ALLOCATION_FAILURE_INJECTOR_ONLY(code) code +#else +#define ALLOCATION_FAILURE_INJECTOR_RETURN { return; } +#define ALLOCATION_FAILURE_INJECTOR_RETURN_(code) { code } +#define ALLOCATION_FAILURE_INJECTOR_ONLY(code) +#endif // ALLOCATION_FAILURE_INJECTOR + +// Support for injecting allocation failures based on the G1GCAllocationFailureALot* +// flags. Analogous to PromotionFailureALot for the other collectors. +// +// Every G1GCAllocationFailureALotInterval collections without evacuation failure +// in between we "arm" the injector to induce allocation failures after +// G1GCAllocationFailureALotCount successful evacuations. +// +// Available only when ALLOCATION_FAILURE_INJECTOR is defined. +class G1YoungGCAllocationFailureInjector { +#if ALLOCATION_FAILURE_INJECTOR + // Should we inject evacuation failures in the current GC. + bool _inject_allocation_failure_for_current_gc; + + // Records the number of the last collection when allocation failure happened. + // Used to determine whether allocation failure injection should be in effect + // for the current GC. + size_t _last_collection_with_allocation_failure; + + // Records the regions that will fail evacuation. + CHeapBitMap _allocation_failure_regions; +#endif + + bool arm_if_needed_for_gc_type(bool for_young_only_phase, + bool during_concurrent_start, + bool mark_or_rebuild_in_progress) ALLOCATION_FAILURE_INJECTOR_RETURN_( return false; ); + + // Selects the regions that will fail allocation by G1GCAllocationFailureALotCSetPercent. + void select_allocation_failure_regions() ALLOCATION_FAILURE_INJECTOR_RETURN; +public: + + G1YoungGCAllocationFailureInjector() ALLOCATION_FAILURE_INJECTOR_RETURN; + + // Arm the allocation failure injector if needed for the current + // GC (based upon the type of GC and which command line flags are set); + void arm_if_needed() ALLOCATION_FAILURE_INJECTOR_RETURN; + + // Return true if it's time to cause an allocation failure; the caller + // provides the (preferably thread-local) counter to minimize performance impact. + bool allocation_should_fail(size_t& counter, uint region_idx) ALLOCATION_FAILURE_INJECTOR_RETURN_( return false; ); + + // Reset the allocation failure injection counters. Should be called at + // the end of an evacuation pause in which an allocation failure occurred. + void reset() ALLOCATION_FAILURE_INJECTOR_RETURN; +}; + +#endif /* SHARE_GC_G1_G1YOUNGGCALLOCATIONFAILUREINJECTOR_HPP */ diff --git a/src/hotspot/share/gc/g1/g1YoungGCEvacFailureInjector.inline.hpp b/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.inline.hpp similarity index 63% rename from src/hotspot/share/gc/g1/g1YoungGCEvacFailureInjector.inline.hpp rename to src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.inline.hpp index 52582df4609..7c71eb14d52 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCEvacFailureInjector.inline.hpp +++ b/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -22,30 +22,30 @@ * */ -#ifndef SHARE_GC_G1_G1YOUNGGCEVACUATIONFAILUREINJECTOR_INLINE_HPP -#define SHARE_GC_G1_G1YOUNGGCEVACUATIONFAILUREINJECTOR_INLINE_HPP +#ifndef SHARE_GC_G1_G1YOUNGGCALLOCATIONFAILUREINJECTOR_INLINE_HPP +#define SHARE_GC_G1_G1YOUNGGCALLOCATIONFAILUREINJECTOR_INLINE_HPP -#include "gc/g1/g1YoungGCEvacFailureInjector.hpp" +#include "gc/g1/g1YoungGCAllocationFailureInjector.hpp" #include "gc/g1/g1_globals.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" -#if EVAC_FAILURE_INJECTOR +#if ALLOCATION_FAILURE_INJECTOR -inline bool G1YoungGCEvacFailureInjector::evacuation_should_fail(size_t& counter, uint region_idx) { - if (!_inject_evacuation_failure_for_current_gc) { +inline bool G1YoungGCAllocationFailureInjector::allocation_should_fail(size_t& counter, uint region_idx) { + if (!_inject_allocation_failure_for_current_gc) { return false; } - if (!_evac_failure_regions.at(region_idx)) { + if (!_allocation_failure_regions.at(region_idx)) { return false; } - if (++counter < G1EvacuationFailureALotCount) { + if (++counter < G1GCAllocationFailureALotCount) { return false; } counter = 0; return true; } -#endif // #if EVAC_FAILURE_INJECTOR +#endif // #if ALLOCATION_FAILURE_INJECTOR -#endif /* SHARE_GC_G1_G1YOUNGGCEVACUATIONFAILUREINJECTOR_INLINE_HPP */ +#endif /* SHARE_GC_G1_G1YOUNGGCALLOCATIONFAILUREINJECTOR_INLINE_HPP */ diff --git a/src/hotspot/share/gc/g1/g1YoungGCEvacFailureInjector.cpp b/src/hotspot/share/gc/g1/g1YoungGCEvacFailureInjector.cpp deleted file mode 100644 index 7ac935eb594..00000000000 --- a/src/hotspot/share/gc/g1/g1YoungGCEvacFailureInjector.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2021, 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. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#include "precompiled.hpp" - -#include "gc/g1/g1CollectedHeap.inline.hpp" -#include "gc/g1/g1YoungGCEvacFailureInjector.inline.hpp" -#include "gc/g1/g1_globals.hpp" - -#if EVAC_FAILURE_INJECTOR - -class SelectEvacFailureRegionClosure : public HeapRegionClosure { - CHeapBitMap& _evac_failure_regions; - size_t _evac_failure_regions_num; - -public: - SelectEvacFailureRegionClosure(CHeapBitMap& evac_failure_regions, size_t cset_length) : - _evac_failure_regions(evac_failure_regions), - _evac_failure_regions_num(cset_length * G1EvacuationFailureALotCSetPercent / 100) { } - - bool do_heap_region(HeapRegion* r) override { - assert(r->in_collection_set(), "must be"); - if (_evac_failure_regions_num > 0) { - _evac_failure_regions.set_bit(r->hrm_index()); - --_evac_failure_regions_num; - } - return _evac_failure_regions_num == 0; - } -}; - -G1YoungGCEvacFailureInjector::G1YoungGCEvacFailureInjector() - : _inject_evacuation_failure_for_current_gc(), - _last_collection_with_evacuation_failure(), - _evac_failure_regions(mtGC) {} - -void G1YoungGCEvacFailureInjector::select_evac_failure_regions() { - G1CollectedHeap* g1h = G1CollectedHeap::heap(); - _evac_failure_regions.reinitialize(g1h->max_reserved_regions()); - SelectEvacFailureRegionClosure closure(_evac_failure_regions, g1h->collection_set()->cur_length()); - g1h->collection_set_iterate_all(&closure); -} - -bool G1YoungGCEvacFailureInjector::arm_if_needed_for_gc_type(bool for_young_only_phase, - bool during_concurrent_start, - bool mark_or_rebuild_in_progress) { - bool res = false; - if (mark_or_rebuild_in_progress) { - res |= G1EvacuationFailureALotDuringConcMark; - } - if (during_concurrent_start) { - res |= G1EvacuationFailureALotDuringConcurrentStart; - } - if (for_young_only_phase) { - res |= G1EvacuationFailureALotDuringYoungGC; - } else { - // GCs are mixed - res |= G1EvacuationFailureALotDuringMixedGC; - } - return res; -} - -void G1YoungGCEvacFailureInjector::arm_if_needed() { - if (G1EvacuationFailureALot) { - G1CollectedHeap* g1h = G1CollectedHeap::heap(); - // Check if we have gone over the interval. - const size_t gc_num = g1h->total_collections(); - const size_t elapsed_gcs = gc_num - _last_collection_with_evacuation_failure; - - _inject_evacuation_failure_for_current_gc = (elapsed_gcs >= G1EvacuationFailureALotInterval); - - // Now check if evacuation failure injection should be enabled for the current GC. - G1CollectorState* collector_state = g1h->collector_state(); - const bool in_young_only_phase = collector_state->in_young_only_phase(); - const bool in_concurrent_start_gc = collector_state->in_concurrent_start_gc(); - const bool mark_or_rebuild_in_progress = collector_state->mark_or_rebuild_in_progress(); - - _inject_evacuation_failure_for_current_gc &= - arm_if_needed_for_gc_type(in_young_only_phase, - in_concurrent_start_gc, - mark_or_rebuild_in_progress); - - if (_inject_evacuation_failure_for_current_gc) { - select_evac_failure_regions(); - } - } -} - -void G1YoungGCEvacFailureInjector::reset() { - _last_collection_with_evacuation_failure = G1CollectedHeap::heap()->total_collections(); - _inject_evacuation_failure_for_current_gc = false; -} - -#endif // #if EVAC_FAILURE_INJECTOR diff --git a/src/hotspot/share/gc/g1/g1YoungGCEvacFailureInjector.hpp b/src/hotspot/share/gc/g1/g1YoungGCEvacFailureInjector.hpp deleted file mode 100644 index f5efd21bcfe..00000000000 --- a/src/hotspot/share/gc/g1/g1YoungGCEvacFailureInjector.hpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2021, 2022, 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. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_GC_G1_G1YOUNGGCEVACUATIONFAILUREINJECTOR_HPP -#define SHARE_GC_G1_G1YOUNGGCEVACUATIONFAILUREINJECTOR_HPP - -#include "gc/g1/g1_globals.hpp" -#include "memory/allStatic.hpp" -#include "utilities/globalDefinitions.hpp" - -#if EVAC_FAILURE_INJECTOR -#define EVAC_FAILURE_INJECTOR_RETURN -#define EVAC_FAILURE_INJECTOR_RETURN_(code) -#define EVAC_FAILURE_INJECTOR_ONLY(code) code -#else -#define EVAC_FAILURE_INJECTOR_RETURN { return; } -#define EVAC_FAILURE_INJECTOR_RETURN_(code) { code } -#define EVAC_FAILURE_INJECTOR_ONLY(code) -#endif // EVAC_FAILURE_INJECTOR - -// Support for injecting evacuation failures based on the G1EvacuationFailureALot* -// flags. Analogous to PromotionFailureALot for the other collectors. -// -// Every G1EvacuationFailureALotInterval collections without evacuation failure -// in between we "arm" the injector to induce evacuation failures after -// G1EvacuationFailureALotCount successful evacuations. -// -// Available only when EVAC_FAILURE_INJECTOR is defined. -class G1YoungGCEvacFailureInjector { -#if EVAC_FAILURE_INJECTOR - // Should we inject evacuation failures in the current GC. - bool _inject_evacuation_failure_for_current_gc; - - // Records the number of the last collection when evacuation failure happened. - // Used to determine whether evacuation failure injection should be in effect - // for the current GC. - size_t _last_collection_with_evacuation_failure; - - // Records the regions that will fail evacuation. - CHeapBitMap _evac_failure_regions; -#endif - - bool arm_if_needed_for_gc_type(bool for_young_only_phase, - bool during_concurrent_start, - bool mark_or_rebuild_in_progress) EVAC_FAILURE_INJECTOR_RETURN_( return false; ); - - // Selects the regions that will fail evacuation by G1EvacuationFailureALotCSetPercent. - void select_evac_failure_regions() EVAC_FAILURE_INJECTOR_RETURN; -public: - - G1YoungGCEvacFailureInjector() EVAC_FAILURE_INJECTOR_RETURN; - - // Arm the evacuation failure injector if needed for the current - // GC (based upon the type of GC and which command line flags are set); - void arm_if_needed() EVAC_FAILURE_INJECTOR_RETURN; - - // Return true if it's time to cause an evacuation failure; the caller - // provides the (preferably thread-local) counter to minimize performance impact. - bool evacuation_should_fail(size_t& counter, uint region_idx) EVAC_FAILURE_INJECTOR_RETURN_( return false; ); - - // Reset the evacuation failure injection counters. Should be called at - // the end of an evacuation pause in which an evacuation failure occurred. - void reset() EVAC_FAILURE_INJECTOR_RETURN; -}; - -#endif /* SHARE_GC_G1_G1YOUNGGCEVACUATIONFAILUREINJECTOR_HPP */ diff --git a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp index 637860c8132..377f5f550d1 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp @@ -67,16 +67,26 @@ public: class G1PostEvacuateCollectionSetCleanupTask1::RecalculateUsedTask : public G1AbstractSubTask { bool _evacuation_failed; + bool _allocation_failed; public: - RecalculateUsedTask(bool evacuation_failed) : G1AbstractSubTask(G1GCPhaseTimes::RecalculateUsed), _evacuation_failed(evacuation_failed) { } + RecalculateUsedTask(bool evacuation_failed, bool allocation_failed) : + G1AbstractSubTask(G1GCPhaseTimes::RecalculateUsed), + _evacuation_failed(evacuation_failed), + _allocation_failed(allocation_failed) { } double worker_cost() const override { // If there is no evacuation failure, the work to perform is minimal. return _evacuation_failed ? 1.0 : AlmostNoWork; } - void do_work(uint worker_id) override { G1CollectedHeap::heap()->update_used_after_gc(_evacuation_failed); } + void do_work(uint worker_id) override { + G1CollectedHeap::heap()->update_used_after_gc(_evacuation_failed); + if (_allocation_failed) { + // Reset the G1GCAllocationFailureALot counters and flags + G1CollectedHeap::heap()->allocation_failure_injector()->reset(); + } + } }; class G1PostEvacuateCollectionSetCleanupTask1::SampleCollectionSetCandidatesTask : public G1AbstractSubTask { @@ -309,9 +319,10 @@ G1PostEvacuateCollectionSetCleanupTask1::G1PostEvacuateCollectionSetCleanupTask1 G1BatchedTask("Post Evacuate Cleanup 1", G1CollectedHeap::heap()->phase_times()) { bool evac_failed = evac_failure_regions->has_regions_evac_failed(); + bool alloc_failed = evac_failure_regions->has_regions_alloc_failed(); add_serial_task(new MergePssTask(per_thread_states)); - add_serial_task(new RecalculateUsedTask(evac_failed)); + add_serial_task(new RecalculateUsedTask(evac_failed, alloc_failed)); if (SampleCollectionSetCandidatesTask::should_execute()) { add_serial_task(new SampleCollectionSetCandidatesTask()); } diff --git a/src/hotspot/share/gc/g1/g1_globals.hpp b/src/hotspot/share/gc/g1/g1_globals.hpp index dc38047aa28..c46c1b47332 100644 --- a/src/hotspot/share/gc/g1/g1_globals.hpp +++ b/src/hotspot/share/gc/g1/g1_globals.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2023, 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 @@ -27,18 +27,18 @@ #include "runtime/globals_shared.hpp" -// Enable evacuation failure injector by default in non-product builds. +// Enable allocation failure injector by default in non-product builds. -#ifdef EVAC_FAILURE_INJECTOR -#error "EVAC_FAILURE_INJECTOR already defined" +#ifdef ALLOCATION_FAILURE_INJECTOR +#error "ALLOCATION_FAILURE_INJECTOR already defined" #endif #ifndef PRODUCT -#define EVAC_FAILURE_INJECTOR 1 +#define ALLOCATION_FAILURE_INJECTOR 1 #else -#define EVAC_FAILURE_INJECTOR 0 +#define ALLOCATION_FAILURE_INJECTOR 0 #endif -#if EVAC_FAILURE_INJECTOR +#if ALLOCATION_FAILURE_INJECTOR #define GC_G1_EVACUATION_FAILURE_FLAGS(develop, \ develop_pd, \ product, \ @@ -47,38 +47,38 @@ range, \ constraint) \ \ - product(bool, G1EvacuationFailureALot, false, \ - "Force use of evacuation failure handling during certain " \ - "evacuation pauses") \ + product(bool, G1GCAllocationFailureALot, false, \ + "Force execution of evacuation failure handling by inducing " \ + "allocation failures during certain young collection pauses") \ \ - product(uintx, G1EvacuationFailureALotCount, 1000, \ - "Number of successful evacuations between evacuation failures " \ - "occurring at object copying per thread") \ + product(uintx, G1GCAllocationFailureALotCount, 1000, \ + "Number of successful evacuations between induced allocation " \ + "failures occurring at object copying per thread") \ \ - product(uintx, G1EvacuationFailureALotInterval, 5, \ + product(uintx, G1GCAllocationFailureALotInterval, 5, \ "Total collections between forced triggering of evacuation " \ "failures") \ \ - product(bool, G1EvacuationFailureALotDuringConcMark, true, \ - "Force use of evacuation failure handling during evacuation " \ - "pauses when marking is in progress") \ + product(bool, G1GCAllocationFailureALotDuringConcMark, true, \ + "Trigger evacuation failure handling in collection pauses where " \ + "marking is in progress") \ \ - product(bool, G1EvacuationFailureALotDuringConcurrentStart, true, \ - "Force use of evacuation failure handling during concurrent " \ - "start evacuation pauses") \ + product(bool, G1GCAllocationFailureALotDuringConcurrentStart, true, \ + "Trigger evacuation failure handling during concurrent start " \ + "collection pauses") \ \ - product(bool, G1EvacuationFailureALotDuringYoungGC, true, \ - "Force use of evacuation failure handling during young " \ - "evacuation pauses") \ + product(bool, G1GCAllocationFailureALotDuringYoungGC, true, \ + "Trigger evacuation failure handling during young collection " \ + "pauses") \ \ - product(bool, G1EvacuationFailureALotDuringMixedGC, true, \ + product(bool, G1GCAllocationFailureALotDuringMixedGC, true, \ "Force use of evacuation failure handling during mixed " \ - "evacuation pauses") \ + "collection pauses") \ \ - product(uint, G1EvacuationFailureALotCSetPercent, 100, \ + product(uint, G1GCAllocationFailureALotCSetPercent, 100, \ "The percentage of regions in the collection set starting " \ - "from the beginning where the forced evacuation failure " \ - "injection will be applied.") \ + "from the beginning where the allocation failures are " \ + "injected.") \ range(1, 100) #else #define GC_G1_EVACUATION_FAILURE_FLAGS(develop, \ diff --git a/test/hotspot/jtreg/gc/g1/TestEvacuationFailure.java b/test/hotspot/jtreg/gc/g1/TestAllocationFailure.java similarity index 86% rename from test/hotspot/jtreg/gc/g1/TestEvacuationFailure.java rename to test/hotspot/jtreg/gc/g1/TestAllocationFailure.java index fd4a5094757..0723eb89f51 100644 --- a/test/hotspot/jtreg/gc/g1/TestEvacuationFailure.java +++ b/test/hotspot/jtreg/gc/g1/TestAllocationFailure.java @@ -24,7 +24,7 @@ package gc.g1; /* - * @test TestEvacuationFailure + * @test TestAllocationFailure * @summary Ensure the output for a minor GC with G1 that has allocation failure contains the correct strings. * @requires vm.gc.G1 * @requires vm.debug @@ -34,24 +34,24 @@ package gc.g1; * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * gc.g1.TestEvacuationFailure + * gc.g1.TestAllocationFailure */ import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; -public class TestEvacuationFailure { +public class TestAllocationFailure { public static void main(String[] args) throws Exception { ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+UseG1GC", "-Xmx32M", "-Xmn16M", - "-XX:+G1EvacuationFailureALot", - "-XX:G1EvacuationFailureALotCount=100", - "-XX:G1EvacuationFailureALotInterval=1", + "-XX:+G1GCAllocationFailureALot", + "-XX:G1GCAllocationFailureALotCount=100", + "-XX:G1GCAllocationFailureALotInterval=1", "-XX:+UnlockDiagnosticVMOptions", "-Xlog:gc", - GCTestWithEvacuationFailure.class.getName()); + GCTestWithAllocationFailure.class.getName()); OutputAnalyzer output = new OutputAnalyzer(pb.start()); System.out.println(output.getStdout()); @@ -59,17 +59,17 @@ public class TestEvacuationFailure { output.shouldHaveExitValue(0); } - static class GCTestWithEvacuationFailure { + static class GCTestWithAllocationFailure { private static byte[] garbage; private static byte[] largeObject; - private static Object[] holder = new Object[200]; // Must be larger than G1EvacuationFailureALotCount + private static Object[] holder = new Object[200]; // Must be larger than G1GCAllocationFailureALotCount public static void main(String [] args) { largeObject = new byte[16 * 1024 * 1024]; System.out.println("Creating garbage"); // Create 16 MB of garbage. This should result in at least one GC, // (Heap size is 32M, we use 17MB for the large object above) - // which is larger than G1EvacuationFailureALotInterval. + // which is larger than G1GCAllocationFailureALotInterval. for (int i = 0; i < 16 * 1024; i++) { holder[i % holder.length] = new byte[1024]; } diff --git a/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java b/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java index 6a024bb3ba2..c29919e8aff 100644 --- a/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java +++ b/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java @@ -275,12 +275,12 @@ public class TestGCLogMessages { ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+UseG1GC", "-Xmx32M", "-Xmn16M", - "-XX:+G1EvacuationFailureALot", - "-XX:G1EvacuationFailureALotCount=100", - "-XX:G1EvacuationFailureALotInterval=1", + "-XX:+G1GCAllocationFailureALot", + "-XX:G1GCAllocationFailureALotCount=100", + "-XX:G1GCAllocationFailureALotInterval=1", "-XX:+UnlockDiagnosticVMOptions", "-Xlog:gc+phases=debug", - GCTestWithEvacuationFailure.class.getName()); + GCTestWithAllocationFailure.class.getName()); OutputAnalyzer output = new OutputAnalyzer(pb.start()); checkMessagesAtLevel(output, exhFailureMessages, Level.DEBUG); @@ -292,7 +292,7 @@ public class TestGCLogMessages { "-Xms32M", "-XX:+UnlockDiagnosticVMOptions", "-Xlog:gc+phases=trace", - GCTestWithEvacuationFailure.class.getName()); + GCTestWithAllocationFailure.class.getName()); output = new OutputAnalyzer(pb.start()); checkMessagesAtLevel(output, exhFailureMessages, Level.TRACE); @@ -346,17 +346,17 @@ public class TestGCLogMessages { } } - static class GCTestWithEvacuationFailure { + static class GCTestWithAllocationFailure { private static byte[] garbage; private static byte[] largeObject; - private static Object[] holder = new Object[200]; // Must be larger than G1EvacuationFailureALotCount + private static Object[] holder = new Object[200]; // Must be larger than G1GCAllocationFailureALotCount public static void main(String [] args) { largeObject = new byte[16*1024*1024]; System.out.println("Creating garbage"); // Create 16 MB of garbage. This should result in at least one GC, // (Heap size is 32M, we use 17MB for the large object above) - // which is larger than G1EvacuationFailureALotInterval. + // which is larger than G1GCAllocationFailureALotInterval. for (int i = 0; i < 16 * 1024; i++) { holder[i % holder.length] = new byte[1024]; } diff --git a/test/hotspot/jtreg/gc/g1/TestVerifyGCType.java b/test/hotspot/jtreg/gc/g1/TestVerifyGCType.java index 02ee74bed8b..d14fcf95de1 100644 --- a/test/hotspot/jtreg/gc/g1/TestVerifyGCType.java +++ b/test/hotspot/jtreg/gc/g1/TestVerifyGCType.java @@ -122,9 +122,9 @@ public class TestVerifyGCType { private static void testYoungEvacFail() throws Exception { OutputAnalyzer output; output = testWithVerificationType(new String[] {"young-evac-fail"}, - new String[] {"-XX:+G1EvacuationFailureALot", - "-XX:G1EvacuationFailureALotCount=100", - "-XX:G1EvacuationFailureALotInterval=1", + new String[] {"-XX:+G1GCAllocationFailureALot", + "-XX:G1GCAllocationFailureALotCount=100", + "-XX:G1GCAllocationFailureALotInterval=1", "-XX:+UnlockDiagnosticVMOptions"}); output.shouldHaveExitValue(0); diff --git a/test/jdk/jdk/jfr/event/gc/detailed/TestEvacuationFailedEvent.java b/test/jdk/jdk/jfr/event/gc/detailed/TestEvacuationFailedEvent.java index fb34249c1dc..584900620d4 100644 --- a/test/jdk/jdk/jfr/event/gc/detailed/TestEvacuationFailedEvent.java +++ b/test/jdk/jdk/jfr/event/gc/detailed/TestEvacuationFailedEvent.java @@ -47,8 +47,8 @@ import jdk.test.whitebox.WhiteBox; * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * -Xmx32m -Xms32m -XX:+UnlockExperimentalVMOptions -XX:+G1EvacuationFailureALot - * -XX:G1EvacuationFailureALotCount=100 -XX:G1EvacuationFailureALotInterval=1 + * -Xmx32m -Xms32m -XX:+UnlockExperimentalVMOptions -XX:+G1GCAllocationFailureALot + * -XX:G1GCAllocationFailureALotCount=100 -XX:G1GCAllocationFailureALotInterval=1 * -Xlog:gc=debug -XX:+UseG1GC jdk.jfr.event.gc.detailed.TestEvacuationFailedEvent */