1. 简介
YGC整个过程都在STW下进行,出于减少停顿时间的考量,对于老年代的回收显然需要与Mutator同时进行,G1引入了混合式GC,与CMS算法类似,均采用了并发标记。
混合式回收主要分为如下子阶段:
- 初始标记子阶段
- 并发标记子阶段
- 再标记子阶段
- 清理子阶段
- 垃圾回收
2. 算法概览
2.1 标记算法概览
由于混合式GC使用的是并发标记,Mutator可能会随时改变对象引用关系,从而导致漏标和错标。
错标仅导致浮动垃圾,并不会导致运行错误。而漏标会导致对象被错误的回收,进而产生严重错误;为了避免漏标,G1引入了三色标记法。
- 白色:垃圾收集器未探测到的对象
- 灰色:活着的对象,但是依然没有被垃圾收集器扫描过
- 黑色:活着的对象,并且已经被垃圾收集器扫描过
2.2 STAB机制简介
SATB(start at the beginning),在并发标记时,如果对象引用关系发生变化,G1会通过putfield字节码中的写屏障将这一引用关系的变化写入G1SATBMarkQueueSet和G1SATBMarkQueue中。并在并发标记子阶段和再标记子阶段处理G1SATBMarkQueueSet和G1SATBMarkQueue中的数据。
3. 源码分析
3.1 是否进入并发标记判定
g1Policy.cpp
G1IHOPControl* G1Policy::create_ihop_control(const G1Predictions* predictor){
if (G1UseAdaptiveIHOP) {
return new G1AdaptiveIHOPControl(InitiatingHeapOccupancyPercent,
predictor,
G1ReservePercent,
G1HeapWastePercent);
} else {
return new G1StaticIHOPControl(InitiatingHeapOccupancyPercent);
}
}
bool G1Policy::need_to_start_conc_mark(const char* source, size_t alloc_word_size) {
if (about_to_start_mixed_phase()) {
return false;
}
size_t marking_initiating_used_threshold = _ihop_control->get_conc_mark_start_threshold();
size_t cur_used_bytes = _g1h->non_young_capacity_bytes();
size_t alloc_byte_size = alloc_word_size * HeapWordSize;
size_t marking_request_bytes = cur_used_bytes + alloc_byte_size;
bool result = false;
if (marking_request_bytes > marking_initiating_used_threshold) {
result = collector_state()->in_young_only_phase() && !collector_state()->in_young_gc_before_mixed();
log_debug(gc, ergo, ihop)("%s occupancy: " SIZE_FORMAT "B allocation request: " SIZE_FORMAT "B threshold: " SIZE_FORMAT "B (%1.2f) source: %s",
result ? "Request concurrent cycle initiation (occupancy higher than threshold)" : "Do not request concurrent cycle initiation (still doing mixed collections)",
cur_used_bytes, alloc_byte_size, marking_initiating_used_threshold, (double) marking_initiating_used_threshold / _g1h->capacity() * 100, source);
}
return result;
}
void G1Policy::record_new_heap_size(uint new_number_of_regions) {
// re-calculate the necessary reserve
double reserve_regions_d = (double) new_number_of_regions * _reserve_factor;
// We use ceiling so that if reserve_regions_d is > 0.0 (but
// smaller than 1.0) we'll get 1.
_reserve_regions = (uint) ceil(reserve_regions_d);
_young_gen_sizer->heap_size_changed(new_number_of_regions);
_ihop_control->update_target_occupancy(new_number_of_regions * HeapRegion::GrainBytes);
}
g1Policy.hpp
size_t get_conc_mark_start_threshold() {
guarantee(_target_occupancy > 0, "Target occupancy must have been initialized.");
return (size_t) (_initial_ihop_percent * _target_occupancy / 100.0);
}
- YGC最后阶段判断是否启动并发标记
- 判断的依据是分配和即将分配的内存占比是否大于阈值
- 阈值受JVM参数InitiatingHeapOccupancyPercent控制,默认45
如果需要进行并发标记,则通知并发标记线程
g1CollectedHeap.cpp
void G1CollectedHeap::do_concurrent_mark() {
Mut