CMS abortable preclean
Gustav Åkesson
gustav.r.akesson at gmail.com
Sun Jul 27 15:34:14 UTC 2014
Hi,
I've seen a behavior with CMS precleaning which results in less
deterministic remark pauses. Some background...
CMS is precleaning and attempts to schedule the remark phase when eden
occupancy has reached a configurable percentage - default is 50%. If not
enough cards were precleaned (<100 cards) during an iteration, the phase
backs off and sleeps for a configurable number of millis - default 100ms.
The problem I've found occurs when not enough cards were cleaned, and we
have reached the stop-precleaning-threshold - then sleep of 100ms occurs
which mean that eden fills up for 100ms and even more cards get dirty -
cards which the concurrent precleaning will *not* deal with. Then we exit
precleaning and schedule remark.
So my suggestion to reduce this risk of indeterminism is that if the
predicate (workdone < CMSAbortablePrecleanMinWorkPerIteration) is true,
then we also check if we should continue precleaning by invoking
should_abort_preclean() - if we should stop, we don't sleep either - we
simply exit precleaning and schedule remark phase. If we shouldn't stop,
then we sleep.
I've tried to reduce the configurable values of time-to-sleep and
win-work-per-precleaning-iteration, and this yields significantly more
deterministic remark pauses. According to the logs, indetermism occurs when
not enough work was done and we should exit precleaning. However, I don't
think it is a good idea to let the precleaning spin and not doing much.
So, what do you say - should we fix this? Below is the code and my comments
are in *red*.
Best Regards,
Gustav Åkesson
----------------
concurrentMarkSweepGeneration.cpp
// Try and schedule the remark such that young gen
// occupancy is CMSScheduleRemarkEdenPenetration %.
void CMSCollector::abortable_preclean() {
check_correct_thread_executing();
assert(CMSPrecleaningEnabled, "Inconsistent control state");
assert(_collectorState == AbortablePreclean, "Inconsistent control
state");
// If Eden's current occupancy is below this threshold,
// immediately schedule the remark; else preclean
// past the next scavenge in an effort to
// schedule the pause as described avove. By choosing
// CMSScheduleRemarkEdenSizeThreshold >= max eden size
// we will never do an actual abortable preclean cycle.
if (get_eden_used() > CMSScheduleRemarkEdenSizeThreshold) {
TraceCPUTime tcpu(PrintGCDetails, true, gclog_or_tty);
CMSPhaseAccounting pa(this, "abortable-preclean",
_gc_tracer_cm->gc_id(), !PrintGCDetails);
// We need more smarts in the abortable preclean
// loop below to deal with cases where allocation
// in young gen is very very slow, and our precleaning
// is running a losing race against a horde of
// mutators intent on flooding us with CMS updates
// (dirty cards).
// One, admittedly dumb, strategy is to give up
// after a certain number of abortable precleaning loops
// or after a certain maximum time. We want to make
// this smarter in the next iteration.
// XXX FIX ME!!! YSR
size_t loops = 0, workdone = 0, cumworkdone = 0, waited = 0;
while (!(should_abort_preclean() ||
ConcurrentMarkSweepThread::should_terminate())) {
*workdone = preclean_work(CMSPrecleanRefLists2,
CMSPrecleanSurvivors2); // Here we do some "heavy" work*
cumworkdone += workdone;
loops++;
// Voluntarily terminate abortable preclean phase if we have
// been at it for too long.
if ((CMSMaxAbortablePrecleanLoops != 0) &&
loops >= CMSMaxAbortablePrecleanLoops) {
if (PrintGCDetails) {
gclog_or_tty->print(" CMS: abort preclean due to loops ");
}
break;
}
if (pa.wallclock_millis() > CMSMaxAbortablePrecleanTime) {
if (PrintGCDetails) {
gclog_or_tty->print(" CMS: abort preclean due to time ");
}
break;
}
// If we are doing little work each iteration, we should
// take a short break.
*// Here we take a break when not enough work was done.*
if (workdone < CMSAbortablePrecleanMinWorkPerIteration */* And here
we should check if we should exit precleaning */*) {
// Sleep for some time, waiting for work to accumulate
stopTimer();
cmsThread()->wait_on_cms_lock(CMSAbortablePrecleanWaitMillis);
startTimer();
waited++;
}
}
if (PrintCMSStatistics > 0) {
gclog_or_tty->print(" [%d iterations, %d waits, %d cards)] ",
loops, waited, cumworkdone);
}
}
CMSTokenSync x(true); // is cms thread
if (_collectorState != Idling) {
assert(_collectorState == AbortablePreclean,
"Spontaneous state transition?");
_collectorState = FinalMarking;
} // Else, a foreground collection completed this CMS cycle.
return;
}
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/hotspot-gc-dev/attachments/20140727/6f8e2cb6/attachment.htm>
More information about the hotspot-gc-dev
mailing list