Skip to content

Commit cad73d3

Browse files
author
William Kemper
committed
8370041: GenShen: Filter young pointers from thread local SATB buffers when only marking old
Reviewed-by: ysr, kdnilsen
1 parent 9cc542e commit cad73d3

11 files changed

+133
-223
lines changed

src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHCLOSURES_HPP
2525
#define SHARE_GC_SHENANDOAH_SHENANDOAHCLOSURES_HPP
2626

27-
#include "code/nmethod.hpp"
2827
#include "gc/shared/stringdedup/stringDedup.hpp"
2928
#include "gc/shenandoah/shenandoahGenerationType.hpp"
3029
#include "gc/shenandoah/shenandoahTaskqueue.hpp"
@@ -230,6 +229,17 @@ class ShenandoahConcUpdateRefsClosure : public ShenandoahUpdateRefsSuperClosure
230229
};
231230

232231

232+
class ShenandoahFlushSATB : public ThreadClosure {
233+
private:
234+
SATBMarkQueueSet& _satb_qset;
235+
236+
public:
237+
explicit ShenandoahFlushSATB(SATBMarkQueueSet& satb_qset) : _satb_qset(satb_qset) {}
238+
239+
inline void do_thread(Thread* thread) override;
240+
};
241+
242+
233243
//
234244
// ========= Utilities
235245
//

src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,10 @@ inline void ShenandoahConcUpdateRefsClosure::work(T* p) {
253253
_heap->conc_update_with_forwarded(p);
254254
}
255255

256+
inline void ShenandoahFlushSATB::do_thread(Thread* thread) {
257+
// Transfer any partial buffer to the qset for completed buffer processing.
258+
_satb_qset.flush_queue(ShenandoahThreadLocalData::satb_mark_queue(thread));
259+
}
256260

257261
//
258262
// ========= Utilities

src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp

Lines changed: 47 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -279,21 +279,6 @@ bool ShenandoahConcurrentGC::complete_abbreviated_cycle() {
279279
heap->update_region_ages(_generation->complete_marking_context());
280280
}
281281

282-
if (!heap->is_concurrent_old_mark_in_progress()) {
283-
heap->concurrent_final_roots();
284-
} else {
285-
// Since the cycle was shortened for having enough immediate garbage, this will be
286-
// the last phase before concurrent marking of old resumes. We must be sure
287-
// that old mark threads don't see any pointers to garbage in the SATB queues. Even
288-
// though nothing was evacuated, overwriting unreachable weak roots with null may still
289-
// put pointers to regions that become trash in the SATB queues. The following will
290-
// piggyback flushing the thread local SATB queues on the same handshake that propagates
291-
// the gc state change.
292-
ShenandoahSATBMarkQueueSet& satb_queues = ShenandoahBarrierSet::satb_mark_queue_set();
293-
ShenandoahFlushSATBHandshakeClosure complete_thread_local_satb_buffers(satb_queues);
294-
heap->concurrent_final_roots(&complete_thread_local_satb_buffers);
295-
heap->old_generation()->concurrent_transfer_pointers_from_satb();
296-
}
297282
return true;
298283
}
299284

@@ -684,16 +669,10 @@ void ShenandoahConcurrentGC::op_init_mark() {
684669
assert(!heap->has_forwarded_objects(), "No forwarded objects on this path");
685670

686671
if (heap->mode()->is_generational()) {
687-
688672
if (_generation->is_global()) {
689673
heap->old_generation()->cancel_gc();
690-
} else if (heap->is_concurrent_old_mark_in_progress()) {
691-
// Purge the SATB buffers, transferring any valid, old pointers to the
692-
// old generation mark queue. Any pointers in a young region will be
693-
// abandoned.
694-
ShenandoahGCPhase phase(ShenandoahPhaseTimings::init_transfer_satb);
695-
heap->old_generation()->transfer_pointers_from_satb();
696674
}
675+
697676
{
698677
// After we swap card table below, the write-table is all clean, and the read table holds
699678
// cards dirty prior to the start of GC. Young and bootstrap collection will update
@@ -1131,7 +1110,7 @@ class ShenandoahUpdateThreadHandshakeClosure : public HandshakeClosure {
11311110
ShenandoahNonConcUpdateRefsClosure _cl;
11321111
public:
11331112
ShenandoahUpdateThreadHandshakeClosure();
1134-
void do_thread(Thread* thread);
1113+
void do_thread(Thread* thread) override;
11351114
};
11361115

11371116
ShenandoahUpdateThreadHandshakeClosure::ShenandoahUpdateThreadHandshakeClosure() :
@@ -1146,9 +1125,49 @@ void ShenandoahUpdateThreadHandshakeClosure::do_thread(Thread* thread) {
11461125
}
11471126
}
11481127

1128+
class ShenandoahUpdateThreadRootsAndFlushOldSatbBuffers final : public HandshakeClosure {
1129+
// When Shenandoah is marking the old generation, it is possible for the SATB barrier
1130+
// to pick up overwritten pointers that point into a cset region. If these pointers
1131+
// are accessed by mark threads, they will crash. Once update refs has completed, it is
1132+
// no longer possible for a mutator thread to overwrite a pointer into a cset region.
1133+
//
1134+
// Therefore, at the end of update refs, we use this closure to update the thread roots
1135+
// and 'complete' all the thread local SATB buffers. Completing these will filter out
1136+
// anything that has already been marked or anything that points to a region which is
1137+
// not old. We do not need to worry about ABA situations where a region may become old
1138+
// after the pointer is enqueued but before it is filtered. There are only two ways a
1139+
// region may become old:
1140+
// 1. The region is promoted in place. This is safe because such regions will never
1141+
// be in the collection set. If this happens, the pointer will be preserved, essentially
1142+
// becoming part of the old snapshot.
1143+
// 2. The region is allocated during evacuation of old. This is also not a concern because
1144+
// we haven't yet finished marking old so no mixed evacuations will happen.
1145+
ShenandoahUpdateThreadHandshakeClosure _update_roots;
1146+
ShenandoahFlushSATB _flush_all_satb;
1147+
1148+
public:
1149+
ShenandoahUpdateThreadRootsAndFlushOldSatbBuffers() :
1150+
HandshakeClosure("Shenandoah Update Thread Roots and Flush SATB"),
1151+
_flush_all_satb(ShenandoahBarrierSet::satb_mark_queue_set()) {
1152+
assert(ShenandoahBarrierSet::satb_mark_queue_set().get_filter_out_young(),
1153+
"Should be filtering pointers outside of old during old marking");
1154+
}
1155+
1156+
void do_thread(Thread* thread) override {
1157+
_update_roots.do_thread(thread);
1158+
_flush_all_satb.do_thread(thread);
1159+
}
1160+
};
1161+
11491162
void ShenandoahConcurrentGC::op_update_thread_roots() {
1150-
ShenandoahUpdateThreadHandshakeClosure cl;
1151-
Handshake::execute(&cl);
1163+
ShenandoahHeap* const heap = ShenandoahHeap::heap();
1164+
if (heap->is_concurrent_old_mark_in_progress()) {
1165+
ShenandoahUpdateThreadRootsAndFlushOldSatbBuffers cl;
1166+
Handshake::execute(&cl);
1167+
} else {
1168+
ShenandoahUpdateThreadHandshakeClosure cl;
1169+
Handshake::execute(&cl);
1170+
}
11521171
}
11531172

11541173
void ShenandoahConcurrentGC::op_final_update_refs() {
@@ -1177,23 +1196,6 @@ void ShenandoahConcurrentGC::op_final_update_refs() {
11771196
heap->set_has_forwarded_objects(false);
11781197

11791198
if (heap->mode()->is_generational() && heap->is_concurrent_old_mark_in_progress()) {
1180-
// When the SATB barrier is left on to support concurrent old gen mark, it may pick up writes to
1181-
// objects in the collection set. After those objects are evacuated, the pointers in the
1182-
// SATB are no longer safe. Once we have finished update references, we are guaranteed that
1183-
// no more writes to the collection set are possible.
1184-
//
1185-
// This will transfer any old pointers in _active_ regions from the SATB to the old gen
1186-
// mark queues. All other pointers will be discarded. This would also discard any pointers
1187-
// in old regions that were included in a mixed evacuation. We aren't using the SATB filter
1188-
// methods here because we cannot control when they execute. If the SATB filter runs _after_
1189-
// a region has been recycled, we will not be able to detect the bad pointer.
1190-
//
1191-
// We are not concerned about skipping this step in abbreviated cycles because regions
1192-
// with no live objects cannot have been written to and so cannot have entries in the SATB
1193-
// buffers.
1194-
ShenandoahGCPhase phase(ShenandoahPhaseTimings::final_update_refs_transfer_satb);
1195-
heap->old_generation()->transfer_pointers_from_satb();
1196-
11971199
// Aging_cycle is only relevant during evacuation cycle for individual objects and during final mark for
11981200
// entire regions. Both of these relevant operations occur before final update refs.
11991201
ShenandoahGenerationalHeap::heap()->set_aging_cycle(false);
@@ -1228,13 +1230,13 @@ bool ShenandoahConcurrentGC::entry_final_roots() {
12281230
ShenandoahWorkerPolicy::calc_workers_for_conc_evac(),
12291231
msg);
12301232

1231-
if (!heap->mode()->is_generational()) {
1232-
heap->concurrent_final_roots();
1233-
} else {
1233+
if (heap->mode()->is_generational()) {
12341234
if (!complete_abbreviated_cycle()) {
12351235
return false;
12361236
}
12371237
}
1238+
1239+
heap->concurrent_final_roots();
12381240
return true;
12391241
}
12401242

src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -66,20 +66,6 @@ class ShenandoahConcurrentMarkingTask : public WorkerTask {
6666
}
6767
};
6868

69-
class ShenandoahSATBAndRemarkThreadsClosure : public ThreadClosure {
70-
private:
71-
SATBMarkQueueSet& _satb_qset;
72-
73-
public:
74-
explicit ShenandoahSATBAndRemarkThreadsClosure(SATBMarkQueueSet& satb_qset) :
75-
_satb_qset(satb_qset) {}
76-
77-
void do_thread(Thread* thread) override {
78-
// Transfer any partial buffer to the qset for completed buffer processing.
79-
_satb_qset.flush_queue(ShenandoahThreadLocalData::satb_mark_queue(thread));
80-
}
81-
};
82-
8369
template <ShenandoahGenerationType GENERATION>
8470
class ShenandoahFinalMarkingTask : public WorkerTask {
8571
private:
@@ -109,7 +95,7 @@ class ShenandoahFinalMarkingTask : public WorkerTask {
10995
while (satb_mq_set.apply_closure_to_completed_buffer(&cl)) {}
11096
assert(!heap->has_forwarded_objects(), "Not expected");
11197

112-
ShenandoahSATBAndRemarkThreadsClosure tc(satb_mq_set);
98+
ShenandoahFlushSATB tc(satb_mq_set);
11399
Threads::possibly_parallel_threads_do(true /* is_par */, &tc);
114100
}
115101
_cm->mark_loop(worker_id, _terminator, GENERATION, false /*not cancellable*/,

src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,16 @@ void ShenandoahDegenGC::op_degenerated() {
101101
heap->old_generation()->card_scan()->mark_write_table_as_clean();
102102
}
103103

104-
#ifdef ASSERT
105104
if (heap->mode()->is_generational()) {
106-
ShenandoahOldGeneration* old_generation = heap->old_generation();
105+
const ShenandoahOldGeneration* old_generation = heap->old_generation();
107106
if (!heap->is_concurrent_old_mark_in_progress()) {
108107
// If we are not marking the old generation, there should be nothing in the old mark queues
109108
assert(old_generation->task_queues()->is_empty(), "Old gen task queues should be empty");
109+
} else {
110+
// This is still necessary for degenerated cycles because the degeneration point may occur
111+
// after final mark of the young generation. See ShenandoahConcurrentGC::op_final_update_refs for
112+
// a more detailed explanation.
113+
old_generation->transfer_pointers_from_satb();
110114
}
111115

112116
if (_generation->is_global()) {
@@ -118,7 +122,6 @@ void ShenandoahDegenGC::op_degenerated() {
118122
"Old generation cannot be in state: %s", old_generation->state_name());
119123
}
120124
}
121-
#endif
122125

123126
ShenandoahMetricsSnapshot metrics(heap->free_set());
124127

@@ -166,15 +169,6 @@ void ShenandoahDegenGC::op_degenerated() {
166169
_generation->cancel_marking();
167170
}
168171

169-
if (heap->is_concurrent_mark_in_progress()) {
170-
// If either old or young marking is in progress, the SATB barrier will be enabled.
171-
// The SATB buffer may hold a mix of old and young pointers. The old pointers need to be
172-
// transferred to the old generation mark queues and the young pointers are NOT part
173-
// of this snapshot, so they must be dropped here. It is safe to drop them here because
174-
// we will rescan the roots on this safepoint.
175-
heap->old_generation()->transfer_pointers_from_satb();
176-
}
177-
178172
if (_degen_point == ShenandoahDegenPoint::_degenerated_roots) {
179173
// We only need this if the concurrent cycle has already swapped the card tables.
180174
// Marking will use the 'read' table, but interesting pointers may have been
@@ -193,8 +187,9 @@ void ShenandoahDegenGC::op_degenerated() {
193187
case _degenerated_mark:
194188
// No fallthrough. Continue mark, handed over from concurrent mark if
195189
// concurrent mark has yet completed
196-
if (_degen_point == ShenandoahDegenPoint::_degenerated_mark &&
197-
heap->is_concurrent_mark_in_progress()) {
190+
if (_degen_point == ShenandoahDegenPoint::_degenerated_mark && heap->is_concurrent_mark_in_progress()) {
191+
assert(!ShenandoahBarrierSet::satb_mark_queue_set().get_filter_out_young(),
192+
"Should not be filtering out young pointers when concurrent mark degenerates");
198193
op_finish_mark();
199194
}
200195
assert(!heap->cancelled_gc(), "STW mark can not OOM");

src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,12 +1040,6 @@ void ShenandoahGenerationalHeap::final_update_refs_update_region_states() {
10401040

10411041
void ShenandoahGenerationalHeap::complete_degenerated_cycle() {
10421042
shenandoah_assert_heaplocked_or_safepoint();
1043-
if (is_concurrent_old_mark_in_progress()) {
1044-
// This is still necessary for degenerated cycles because the degeneration point may occur
1045-
// after final mark of the young generation. See ShenandoahConcurrentGC::op_final_update_refs for
1046-
// a more detailed explanation.
1047-
old_generation()->transfer_pointers_from_satb();
1048-
}
10491043
// In case degeneration interrupted concurrent evacuation or update references, we need to clean up
10501044
// transient state. Otherwise, these actions have no effect.
10511045
reset_generation_reserves();

src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2028,6 +2028,10 @@ void ShenandoahHeap::prepare_update_heap_references() {
20282028
void ShenandoahHeap::propagate_gc_state_to_all_threads() {
20292029
assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "Must be at Shenandoah safepoint");
20302030
if (_gc_state_changed) {
2031+
// If we are only marking old, we do not need to process young pointers
2032+
ShenandoahBarrierSet::satb_mark_queue_set().set_filter_out_young(
2033+
is_concurrent_old_mark_in_progress() && !is_concurrent_young_mark_in_progress()
2034+
);
20312035
ShenandoahGCStatePropagatorHandshakeClosure propagator(_gc_state.raw_value());
20322036
Threads::threads_do(&propagator);
20332037
_gc_state_changed = false;

0 commit comments

Comments
 (0)