換成-Xms30m -Xmx30m -Xmn10m -XX:+UseSerialGC後執行的結果為YGC、YGC、YGC、YGC、FGC。
原因就在於Serial GC的悲觀策略是不同的,Serial GC在執行YGC時,首先進入如下代碼片段進行檢查:
void DefNewGeneration::collect(bool full,
bool clear_all_soft_refs,
size_t size,
bool is_tlab) {
...
if (!collection_attempt_is_safe()) {
gch->set_incremental_collection_will_fail();
return;
}
...
}
bool DefNewGeneration::collection_attempt_is_safe() {
if (!to()->is_empty()) {
return false;
}
if (_next_gen == NULL) {
GenCollectedHeap* gch = GenCollectedHeap::heap();
_next_gen = gch->next_gen(this);
assert(_next_gen != NULL,
"This must be the youngest gen, and not the only gen");
}
const double evacuation_ratio = MaxLiveObjectEvacuationRatio / 100.0;
size_t worst_case_evacuation = (size_t)(used() * evacuation_ratio);
// 這裡的_next_gen也就是舊生代了,下面貼出舊生代對應的代碼
return _next_gen->promotion_attempt_is_safe(worst_case_evacuation,
HandlePromotionFailure);
}
bool TenuredGeneration::promotion_attempt_is_safe(
size_t max_promotion_in_bytes,
bool younger_handles_promotion_failure) const {
bool result = max_contiguous_available() >= max_promotion_in_bytes;
if (younger_handles_promotion_failure && !result) {
result = max_contiguous_available() >=
(size_t) gc_stats()->avg_promoted()->padded_average();
if (PrintGC && Verbose && result) {
gclog_or_tty->print_cr("TenuredGeneration::promotion_attempt_is_safe"
" contiguous_available: " SIZE_FORMAT
" avg_promoted: " SIZE_FORMAT,
max_contiguous_available(),
gc_stats()->avg_promoted()->padded_average());
}
} else {
if (PrintGC && Verbose) {
gclog_or_tty->print_cr("TenuredGeneration::promotion_attempt_is_safe"
" contiguous_available: " SIZE_FORMAT
" promotion_in_bytes: " SIZE_FORMAT,
max_contiguous_available(), max_promotion_in_bytes);
}
}
return result;
}
這個檢查首先是檢查目前新生代中使用的空間是否大於了舊生代剩余的空間,如大於且HandlePromotionFailure為true(默認值),那麼再檢查舊生代剩余的空間是否大於之前平均晉升的old的大小,如大於則返回true,小於則返回false,在返回false的情況下,就不進行YGC的剩下的操作了。
按照這樣的規則,在第九次循環的時候,應該執行的是FGC,而不是YGC,這裡的原因是在Serial GC時,是先進行計數和時間的統計等,再調用DefNewGeneration的collect的,因此盡管這次沒有真正的執行YGC的動作,但還是被計數和計入時間了,但這次為什麼GC log中輸出的不是Full GC呢,請看下面的代碼片段:
void GenCollectedHeap::do_collection(bool full,
bool clear_all_soft_refs,
size_t size,
bool is_tlab,
int max_level) {
...
// 在當前場景下,傳入的full為false,因此complete為false
bool complete = full && (max_level == (n_gens()-1));
const char* gc_cause_str = "GC ";
if (complete) {
GCCause::Cause cause = gc_cause();
if (cause == GCCause::_java_lang_system_gc) {
gc_cause_str = "Full GC (System) ";
} else {
gc_cause_str = "Full GC ";
}
}
...
for (int i = starting_level; i <= max_level; i++) {
if (_gens[i]->should_collect(full, size, is_tlab)) {
...
// Determine if allocation request was met.
if (size > 0) {
if (!is_tlab || _gens[i]->supports_tlab_allocation()) {
if (size*HeapWordSize <= _gens[i]->unsafe_max_alloc_nogc()) {
size = 0;
}
}
}
...
}
}
從上可看出,當YGC結束後,eden的空間可以滿足分配的需求的話,需要分配的對象的大小size就被置為零了,而在第九次循環中,由於YGC提前結束,因此eden的空間是仍然不足的,此時需要分配的size大小會不變,上面的GC動作還將進入TenuredGeneration的should_allocate來進行檢查了,該方法的代碼片段如下:
bool TenuredGeneration::should_collect(bool full,
size_t size,
bool is_tlab) {
// This should be one big conditional or (||), but I want to be able to tell
// why it returns what it returns (without re-evaluating the conditionals
// in case they aren't idempotent), so I'm doing it this way.
// DeMorgan says it's okay.
bool result = false;
// 因為full是false,因此進入不了這裡
if (!result && full) {
result = true;
if (PrintGC && Verbose) {
gclog_or_tty->print_cr("TenuredGeneration::should_collect: because"
" full");
}
}
if (!result && should_allocate(size, is_tlab)) {
result = true;
if (PrintGC && Verbose) {
gclog_or_tty->print_cr("TenuredGeneration::should_collect: because"
" should_allocate(" SIZE_FORMAT ")",