Skip to content

Commit 8db3335

Browse files
author
Thomas Schatzl
committed
8247928: Refactor G1ConcurrentMarkThread for mark abort
Reviewed-by: sjohanss, kbarrett
1 parent 7ccf435 commit 8db3335

File tree

3 files changed

+207
-157
lines changed

3 files changed

+207
-157
lines changed

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

+172-149
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,12 @@
4242
#include "runtime/handles.inline.hpp"
4343
#include "runtime/vmThread.hpp"
4444
#include "utilities/debug.hpp"
45-
46-
// ======= Concurrent Mark Thread ========
45+
#include "utilities/ticks.hpp"
4746

4847
G1ConcurrentMarkThread::G1ConcurrentMarkThread(G1ConcurrentMark* cm) :
4948
ConcurrentGCThread(),
5049
_vtime_start(0.0),
5150
_vtime_accum(0.0),
52-
_vtime_mark_accum(0.0),
5351
_cm(cm),
5452
_state(Idle)
5553
{
@@ -77,7 +75,7 @@ class CMCleanup : public VoidClosure {
7775
}
7876
};
7977

80-
double G1ConcurrentMarkThread::mmu_delay_end(G1Policy* g1_policy, bool remark) {
78+
double G1ConcurrentMarkThread::mmu_delay_end(G1Policy* policy, bool remark) {
8179
// There are 3 reasons to use SuspendibleThreadSetJoiner.
8280
// 1. To avoid concurrency problem.
8381
// - G1MMUTracker::add_pause(), when_sec() and when_max_gc_sec() can be called
@@ -88,29 +86,29 @@ double G1ConcurrentMarkThread::mmu_delay_end(G1Policy* g1_policy, bool remark) {
8886
// And then sleep for predicted amount of time by delay_to_keep_mmu().
8987
SuspendibleThreadSetJoiner sts_join;
9088

91-
const G1Analytics* analytics = g1_policy->analytics();
89+
const G1Analytics* analytics = policy->analytics();
9290
double prediction_ms = remark ? analytics->predict_remark_time_ms()
9391
: analytics->predict_cleanup_time_ms();
9492
double prediction = prediction_ms / MILLIUNITS;
95-
G1MMUTracker *mmu_tracker = g1_policy->mmu_tracker();
93+
G1MMUTracker *mmu_tracker = policy->mmu_tracker();
9694
double now = os::elapsedTime();
9795
return now + mmu_tracker->when_sec(now, prediction);
9896
}
9997

100-
void G1ConcurrentMarkThread::delay_to_keep_mmu(G1Policy* g1_policy, bool remark) {
101-
if (g1_policy->use_adaptive_young_list_length()) {
102-
double delay_end_sec = mmu_delay_end(g1_policy, remark);
98+
void G1ConcurrentMarkThread::delay_to_keep_mmu(bool remark) {
99+
G1Policy* policy = G1CollectedHeap::heap()->policy();
100+
101+
if (policy->use_adaptive_young_list_length()) {
102+
double delay_end_sec = mmu_delay_end(policy, remark);
103103
// Wait for timeout or thread termination request.
104104
MonitorLocker ml(CGC_lock, Monitor::_no_safepoint_check_flag);
105-
while (!_cm->has_aborted()) {
105+
while (!_cm->has_aborted() && !should_terminate()) {
106106
double sleep_time_sec = (delay_end_sec - os::elapsedTime());
107107
jlong sleep_time_ms = ceil(sleep_time_sec * MILLIUNITS);
108108
if (sleep_time_ms <= 0) {
109109
break; // Passed end time.
110110
} else if (ml.wait(sleep_time_ms, Monitor::_no_safepoint_check_flag)) {
111111
break; // Timeout => reached end time.
112-
} else if (should_terminate()) {
113-
break; // Wakeup for pending termination request.
114112
}
115113
// Other (possibly spurious) wakeup. Retry with updated sleep time.
116114
}
@@ -136,144 +134,16 @@ class G1ConcPhaseTimer : public GCTraceConcTimeImpl<LogLevel::Info, LOG_TAGS(gc,
136134
void G1ConcurrentMarkThread::run_service() {
137135
_vtime_start = os::elapsedVTime();
138136

139-
G1CollectedHeap* g1h = G1CollectedHeap::heap();
140-
G1Policy* policy = g1h->policy();
141-
142-
while (!should_terminate()) {
143-
// wait until started is set.
144-
sleep_before_next_cycle();
145-
if (should_terminate()) {
146-
break;
147-
}
137+
while (wait_for_next_cycle()) {
148138

149139
GCIdMark gc_id_mark;
150-
151-
_cm->concurrent_cycle_start();
152-
153140
GCTraceConcTime(Info, gc) tt("Concurrent Cycle");
154-
{
155-
ResourceMark rm;
156-
157-
double cycle_start = os::elapsedVTime();
158-
159-
{
160-
G1ConcPhaseTimer p(_cm, "Concurrent Clear Claimed Marks");
161-
ClassLoaderDataGraph::clear_claimed_marks();
162-
}
163-
164-
// We have to ensure that we finish scanning the root regions
165-
// before the next GC takes place. To ensure this we have to
166-
// make sure that we do not join the STS until the root regions
167-
// have been scanned. If we did then it's possible that a
168-
// subsequent GC could block us from joining the STS and proceed
169-
// without the root regions have been scanned which would be a
170-
// correctness issue.
171-
172-
{
173-
G1ConcPhaseTimer p(_cm, "Concurrent Scan Root Regions");
174-
_cm->scan_root_regions();
175-
}
176-
177-
// Note: ConcurrentGCBreakpoints before here risk deadlock,
178-
// because a young GC must wait for root region scanning.
179-
180-
// It would be nice to use the G1ConcPhaseTimer class here but
181-
// the "end" logging is inside the loop and not at the end of
182-
// a scope. Also, the timer doesn't support nesting.
183-
// Mimicking the same log output instead.
184-
jlong mark_start = os::elapsed_counter();
185-
log_info(gc, marking)("Concurrent Mark (%.3fs)",
186-
TimeHelper::counter_to_seconds(mark_start));
187-
for (uint iter = 1; !_cm->has_aborted(); ++iter) {
188-
// Concurrent marking.
189-
{
190-
ConcurrentGCBreakpoints::at("AFTER MARKING STARTED");
191-
G1ConcPhaseTimer p(_cm, "Concurrent Mark From Roots");
192-
_cm->mark_from_roots();
193-
}
194-
if (_cm->has_aborted()) {
195-
break;
196-
}
197-
198-
if (G1UseReferencePrecleaning) {
199-
G1ConcPhaseTimer p(_cm, "Concurrent Preclean");
200-
_cm->preclean();
201-
}
202-
if (_cm->has_aborted()) {
203-
break;
204-
}
205-
206-
// Delay remark pause for MMU.
207-
double mark_end_time = os::elapsedVTime();
208-
jlong mark_end = os::elapsed_counter();
209-
_vtime_mark_accum += (mark_end_time - cycle_start);
210-
delay_to_keep_mmu(policy, true /* remark */);
211-
if (_cm->has_aborted()) {
212-
break;
213-
}
214-
215-
// Pause Remark.
216-
ConcurrentGCBreakpoints::at("BEFORE MARKING COMPLETED");
217-
log_info(gc, marking)("Concurrent Mark (%.3fs, %.3fs) %.3fms",
218-
TimeHelper::counter_to_seconds(mark_start),
219-
TimeHelper::counter_to_seconds(mark_end),
220-
TimeHelper::counter_to_millis(mark_end - mark_start));
221-
CMRemark cl(_cm);
222-
VM_G1Concurrent op(&cl, "Pause Remark");
223-
VMThread::execute(&op);
224-
if (_cm->has_aborted()) {
225-
break;
226-
} else if (!_cm->restart_for_overflow()) {
227-
break; // Exit loop if no restart requested.
228-
} else {
229-
// Loop to restart for overflow.
230-
log_info(gc, marking)("Concurrent Mark Restart for Mark Stack Overflow (iteration #%u)",
231-
iter);
232-
}
233-
}
234-
235-
if (!_cm->has_aborted()) {
236-
G1ConcPhaseTimer p(_cm, "Concurrent Rebuild Remembered Sets");
237-
_cm->rebuild_rem_set_concurrently();
238-
}
239-
240-
double end_time = os::elapsedVTime();
241-
// Update the total virtual time before doing this, since it will try
242-
// to measure it to get the vtime for this marking.
243-
_vtime_accum = (end_time - _vtime_start);
244-
245-
if (!_cm->has_aborted()) {
246-
delay_to_keep_mmu(policy, false /* cleanup */);
247-
}
248-
249-
if (!_cm->has_aborted()) {
250-
CMCleanup cl_cl(_cm);
251-
VM_G1Concurrent op(&cl_cl, "Pause Cleanup");
252-
VMThread::execute(&op);
253-
}
254141

255-
// We now want to allow clearing of the marking bitmap to be
256-
// suspended by a collection pause.
257-
// We may have aborted just before the remark. Do not bother clearing the
258-
// bitmap then, as it has been done during mark abort.
259-
if (!_cm->has_aborted()) {
260-
G1ConcPhaseTimer p(_cm, "Concurrent Cleanup for Next Mark");
261-
_cm->cleanup_for_next_mark();
262-
}
263-
}
142+
concurrent_cycle_start();
143+
full_concurrent_cycle_do();
144+
concurrent_cycle_end();
264145

265-
// Update the number of full collections that have been
266-
// completed. This will also notify the G1OldGCCount_lock in case a
267-
// Java thread is waiting for a full GC to happen (e.g., it
268-
// called System.gc() with +ExplicitGCInvokesConcurrent).
269-
{
270-
SuspendibleThreadSetJoiner sts_join;
271-
g1h->increment_old_marking_cycles_completed(true /* concurrent */,
272-
!_cm->has_aborted() /* liveness_completed */);
273-
274-
_cm->concurrent_cycle_end();
275-
ConcurrentGCBreakpoints::notify_active_to_idle();
276-
}
146+
_vtime_accum = (os::elapsedVTime() - _vtime_start);
277147
}
278148
_cm->root_regions()->cancel_scan();
279149
}
@@ -283,10 +153,7 @@ void G1ConcurrentMarkThread::stop_service() {
283153
CGC_lock->notify_all();
284154
}
285155

286-
287-
void G1ConcurrentMarkThread::sleep_before_next_cycle() {
288-
// We join here because we don't want to do the "shouldConcurrentMark()"
289-
// below while the world is otherwise stopped.
156+
bool G1ConcurrentMarkThread::wait_for_next_cycle() {
290157
assert(!in_progress(), "should have been cleared");
291158

292159
MonitorLocker ml(CGC_lock, Mutex::_no_safepoint_check_flag);
@@ -297,4 +164,160 @@ void G1ConcurrentMarkThread::sleep_before_next_cycle() {
297164
if (started()) {
298165
set_in_progress();
299166
}
167+
168+
return !should_terminate();
169+
}
170+
171+
void G1ConcurrentMarkThread::phase_clear_cld_claimed_marks() {
172+
G1ConcPhaseTimer p(_cm, "Concurrent Clear Claimed Marks");
173+
ClassLoaderDataGraph::clear_claimed_marks();
174+
}
175+
176+
bool G1ConcurrentMarkThread::phase_scan_root_regions() {
177+
G1ConcPhaseTimer p(_cm, "Concurrent Scan Root Regions");
178+
_cm->scan_root_regions();
179+
return _cm->has_aborted();
180+
}
181+
182+
bool G1ConcurrentMarkThread::phase_mark_loop() {
183+
Ticks mark_start = Ticks::now();
184+
log_info(gc, marking)("Concurrent Mark");
185+
186+
for (uint iter = 1; true; ++iter) {
187+
// Subphase 1: Mark From Roots.
188+
if (subphase_mark_from_roots()) return true;
189+
190+
// Subphase 2: Preclean (optional)
191+
if (G1UseReferencePrecleaning) {
192+
if (subphase_preclean()) return true;
193+
}
194+
195+
// Subphase 3: Wait for Remark.
196+
if (subphase_delay_to_keep_mmu_before_remark()) return true;
197+
198+
// Subphase 4: Remark pause
199+
if (subphase_remark()) return true;
200+
201+
// Check if we need to restart the marking loop.
202+
if (!mark_loop_needs_restart()) break;
203+
204+
log_info(gc, marking)("Concurrent Mark Restart for Mark Stack Overflow (iteration #%u)",
205+
iter);
206+
}
207+
208+
log_info(gc, marking)("Concurrent Mark %.3fms",
209+
(Ticks::now() - mark_start).seconds() * 1000.0);
210+
211+
return false;
212+
}
213+
214+
bool G1ConcurrentMarkThread::mark_loop_needs_restart() const {
215+
return _cm->has_overflown();
216+
}
217+
218+
bool G1ConcurrentMarkThread::subphase_mark_from_roots() {
219+
ConcurrentGCBreakpoints::at("AFTER MARKING STARTED");
220+
G1ConcPhaseTimer p(_cm, "Concurrent Mark From Roots");
221+
_cm->mark_from_roots();
222+
return _cm->has_aborted();
223+
}
224+
225+
bool G1ConcurrentMarkThread::subphase_preclean() {
226+
G1ConcPhaseTimer p(_cm, "Concurrent Preclean");
227+
_cm->preclean();
228+
return _cm->has_aborted();
229+
}
230+
231+
bool G1ConcurrentMarkThread::subphase_delay_to_keep_mmu_before_remark() {
232+
delay_to_keep_mmu(true /* remark */);
233+
return _cm->has_aborted();
234+
}
235+
236+
bool G1ConcurrentMarkThread::subphase_remark() {
237+
ConcurrentGCBreakpoints::at("BEFORE MARKING COMPLETED");
238+
CMRemark cl(_cm);
239+
VM_G1Concurrent op(&cl, "Pause Remark");
240+
VMThread::execute(&op);
241+
return _cm->has_aborted();
242+
}
243+
244+
bool G1ConcurrentMarkThread::phase_rebuild_remembered_sets() {
245+
G1ConcPhaseTimer p(_cm, "Concurrent Rebuild Remembered Sets");
246+
_cm->rebuild_rem_set_concurrently();
247+
return _cm->has_aborted();
248+
}
249+
250+
bool G1ConcurrentMarkThread::phase_delay_to_keep_mmu_before_cleanup() {
251+
delay_to_keep_mmu(false /* cleanup */);
252+
return _cm->has_aborted();
253+
}
254+
255+
bool G1ConcurrentMarkThread::phase_cleanup() {
256+
CMCleanup cl(_cm);
257+
VM_G1Concurrent op(&cl, "Pause Cleanup");
258+
VMThread::execute(&op);
259+
return _cm->has_aborted();
260+
}
261+
262+
bool G1ConcurrentMarkThread::phase_clear_bitmap_for_next_mark() {
263+
G1ConcPhaseTimer p(_cm, "Concurrent Cleanup for Next Mark");
264+
_cm->cleanup_for_next_mark();
265+
return _cm->has_aborted();
266+
}
267+
268+
void G1ConcurrentMarkThread::concurrent_cycle_start() {
269+
_cm->concurrent_cycle_start();
270+
}
271+
272+
void G1ConcurrentMarkThread::full_concurrent_cycle_do() {
273+
HandleMark hm(Thread::current());
274+
ResourceMark rm;
275+
276+
// Phase 1: Clear CLD claimed marks.
277+
phase_clear_cld_claimed_marks();
278+
279+
// We have to ensure that we finish scanning the root regions
280+
// before the next GC takes place. To ensure this we have to
281+
// make sure that we do not join the STS until the root regions
282+
// have been scanned. If we did then it's possible that a
283+
// subsequent GC could block us from joining the STS and proceed
284+
// without the root regions have been scanned which would be a
285+
// correctness issue.
286+
//
287+
// So do not return before the scan root regions phase as a GC waits for a
288+
// notification from it.
289+
//
290+
// For the same reason ConcurrentGCBreakpoints (in the phase methods) before
291+
// here risk deadlock, because a young GC must wait for root region scanning.
292+
293+
// Phase 2: Scan root regions.
294+
if (phase_scan_root_regions()) return;
295+
296+
// Phase 3: Actual mark loop.
297+
if (phase_mark_loop()) return;
298+
299+
// Phase 4: Rebuild remembered sets.
300+
if (phase_rebuild_remembered_sets()) return;
301+
302+
// Phase 5: Wait for Cleanup.
303+
if (phase_delay_to_keep_mmu_before_cleanup()) return;
304+
305+
// Phase 6: Cleanup pause
306+
if (phase_cleanup()) return;
307+
308+
// Phase 7: Clear bitmap for next mark.
309+
phase_clear_bitmap_for_next_mark();
310+
}
311+
312+
void G1ConcurrentMarkThread::concurrent_cycle_end() {
313+
// Update the number of full collections that have been
314+
// completed. This will also notify the G1OldGCCount_lock in case a
315+
// Java thread is waiting for a full GC to happen (e.g., it
316+
// called System.gc() with +ExplicitGCInvokesConcurrent).
317+
SuspendibleThreadSetJoiner sts_join;
318+
G1CollectedHeap::heap()->increment_old_marking_cycles_completed(true /* concurrent */,
319+
!_cm->has_aborted());
320+
321+
_cm->concurrent_cycle_end();
322+
ConcurrentGCBreakpoints::notify_active_to_idle();
300323
}

0 commit comments

Comments
 (0)