$ java gc.WhichGC # 两个输出分别表示 young/old 代的 collectorPS Scavenge
PS MarkSweep
$ java -XX:+UseSerialGC gc.WhichGC
Copy
MarkSweepCompact
$ java -XX:+UseParNewGC gc.WhichGC # 注意提示Java HotSpot(TM) 64-Bit Server VM warning: Using the ParNew young collector with the Serial old collector is deprecated and will likely be removed in a future release
ParNew
MarkSweepCompact
$ java -XX:+UseParallelGC gc.WhichGC
PS Scavenge
PS MarkSweep # 虽然名为 MarkSweep,但其实现是 mark-sweep-compact$ java -XX:+UseParallelOldGC gc.WhichGC # 与上面输出一致,不加 flag 时这样同样的输出PS Scavenge
PS MarkSweep
$ java -XX:+UseConcMarkSweepGC gc.WhichGC # ParNew 中 Par 表示 parallel,表明采用并行方式收集 young 代ParNew
ConcurrentMarkSweep # 注意这里没有 compact 过程,也就是说 CMS 的 old 可能会产生碎片$ java -XX:+UseG1GC gc.WhichGC
G1 Young Generation
G1 Old Generation
// ParNewGeneration 构造方法
for (uint i1 =0; i1 < ParallelGCThreads; i1++) {
ObjToScanQueue *q =new ObjToScanQueue();
guarantee(q !=NULL, "work_queue Allocation failure.");
_task_queues->register_queue(i1, q);
}
// do_void 方法
while (true) {
......
// We have no local work, attempt to steal from other threads.
// attempt to steal work from promoted.
if (task_queues()->steal(par_scan_state()->thread_num(),
par_scan_state()->hash_seed(),
obj_to_scan)) {
bool res = work_q->push(obj_to_scan);
assert(res, "Empty queue should have room for a push.");
// if successful, goto Start.
continue;
// try global overflow list.
} elseif (par_gen()->take_from_overflow_list(par_scan_state())) {
continue;
}
.......
}
UseCMSCompactAtFullCollection默认为true,CMSFullGCsBeforeCompaction默认是0,这样的组合保证CMS默认不使用foreground collector,而是用Serial Old GC的方式来进行 Full GC,而且在 JDK9 中,彻底去掉了这两个参数以及 foreground GC 模式,具体见:JDK-8010202: Remove CMS foreground collection,所以这两个参数就不需要再去用了。
这里还需要注意,上述两个备选策略的异同,它们所采用的算法与作用范围均不同:
Serial Old GC的算法是mark-compact(也可以叫做mark-sweep-compact,但要注意它不是“mark-sweep”)。具体算法名是LISP2。它收集的范围是整个GC堆,包括Java heap的young generation和old generation,以及non-Java heap的permanent generation。因而其名 Full GC
// concurrentMarkSweepGeneration.cpp#collect_in_background
while (_collectorState != Idling) {
....
switch (_collectorState) {
caseInitialMarking:
{
ReleaseForegroundGC x(this);
stats().record_cms_begin();
VM_CMS_Initial_Mark initial_mark_op(this);
VMThread::execute(&initial_mark_op);
}
// The collector state may be any legal state at this point
// since the background collector may have yielded to the
// foreground collector.
break;
caseMarking:
// initial marking in checkpointRootsInitialWork has been completed
if (markFromRoots(true)) { // we were successful
assert(_collectorState == Precleaning, "Collector state should ""have changed");
} else {
assert(_foregroundGCIsActive, "Internal state inconsistency");
}
break;
casePrecleaning:
if (UseAdaptiveSizePolicy) {
size_policy()->concurrent_precleaning_begin();
}
// marking from roots in markFromRoots has been completed
preclean();
if (UseAdaptiveSizePolicy) {
size_policy()->concurrent_precleaning_end();
}
assert(_collectorState == AbortablePreclean || _collectorState == FinalMarking,
"Collector state should have changed");
break;
caseAbortablePreclean:
if (UseAdaptiveSizePolicy) {
size_policy()->concurrent_phases_resume();
}
abortable_preclean();
if (UseAdaptiveSizePolicy) {
size_policy()->concurrent_precleaning_end();
}
assert(_collectorState == FinalMarking, "Collector state should ""have changed");
break;
caseFinalMarking:
{
ReleaseForegroundGC x(this);
VM_CMS_Final_Remark final_remark_op(this);
VMThread::execute(&final_remark_op);
}
assert(_foregroundGCShouldWait, "block post-condition");
break;
caseSweeping:
if (UseAdaptiveSizePolicy) {
size_policy()->concurrent_sweeping_begin();
}
// final marking in checkpointRootsFinal has been completed
sweep(true);
assert(_collectorState == Resizing, "Collector state change ""to Resizing must be done under the free_list_lock");
_full_gcs_since_conc_gc =0;
// Stop the timers for adaptive size policy for the concurrent phases
if (UseAdaptiveSizePolicy) {
size_policy()->concurrent_sweeping_end();
size_policy()->concurrent_phases_end(gch->gc_cause(),
gch->prev_gen(_cmsGen)->capacity(),
_cmsGen->free());
}
caseResizing: {
....
break;
}
caseResetting:
......
break;
caseIdling:
default: ShouldNotReachHere();
break;
}
.......
}
// concurrentMarkSweepGeneration.cpp#abortable_preclean()
if (get_eden_used() > CMSScheduleRemarkEdenSizeThreshold) {
size_t loops =0, workdone =0, cumworkdone =0, waited =0;
while (!(should_abort_preclean() || ConcurrentMarkSweepThread::should_terminate())) {
workdone = preclean_work(CMSPrecleanRefLists2, CMSPrecleanSurvivors2);
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.
if (workdone < CMSAbortablePrecleanMinWorkPerIteration) {
// 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);
}
}
条件包括下面几个:
首先要 eden 大于 CMSScheduleRemarkEdenSizeThreshold(默认 2M)时才继续
下面的 while 里面条件主要是为了与 foregroundGC 做同步用的,这里可以先忽略
while 后面的第一个 if 表示这个阶段执行的次数小于 CMSMaxAbortablePrecleanLoops 时才继续,由于这个值默认为 0,所以默认不会进入这个分支
紧接着的那个 if 表示这个阶段的运行时间不能大于 CMSMaxAbortablePrecleanTime,默认是 5s
Allocation Rate: the size of the young generation divided by the time between young generation collections
Promotion Rate: the change in usage of the old gen over time (excluding collections)
Survivor Death Ratio: when looking at a log, the size of survivors in age N divided by the size of survivors in age N-1 in the previous collection
Old Gen collection times: the total time between a CMS-initial-mark and the next CMS-concurrent-reset. You’ll want both your ’normal’ and the maximum observed
Young Gen collection times: both normal and maximum. These are just the “total collection time” entries in the logs Old Gen Buffer:
1
the promotion rate*the maximum Old Gen collection time*(1 + a little bit)