1. 簡介
GC回收週期大體如下圖所示:
GC回收週期包括如下11個子階段:
- phase 1:初始標記,需要STW
- phase 2:併發標記
- phase 3:標記結束,需要STW
- phase 4:併發處理軟引用、弱引用
- phase 5:併發重置Relocation Set
- phase 6:併發銷燬可回收頁
- phase 7:內存驗證
- phase 8:併發選擇Relocation Set
- phase 9:併發準備Relocation Set
- phase 10:開始Relocate,STW
- phase 11:併發Relocate
出於回收效率的考慮,remap過程放在下一個回收週期的併發標記子階段進行。
2. 代碼分析
2.1 入口
ZGC的入口在ZCollectedHeap collect方法
- 調用ZDriver的collect方法
zCollectedHeap.cpp
void ZCollectedHeap::collect(GCCause::Cause cause) {
_driver->collect(cause);
}
根據傳入的GCCause,判斷使用同步消息還是異步消息。
- ZGC自身的觸發策略都使用異步消息,包括rule_timer、rule_warmup、rule_allocation_rate、rule_proactive
- metaspace GC使用異步消息
- 其他情況使用同步消息
zDriver.cpp
void ZDriver::collect(GCCause::Cause cause) {
switch (cause) {
case GCCause::_wb_young_gc:
case GCCause::_wb_conc_mark:
case GCCause::_wb_full_gc:
case GCCause::_dcmd_gc_run:
case GCCause::_java_lang_system_gc:
case GCCause::_full_gc_alot:
case GCCause::_scavenge_alot:
case GCCause::_jvmti_force_gc:
case GCCause::_metadata_GC_clear_soft_refs:
// Start synchronous GC
_gc_cycle_port.send_sync(cause);
break;
case GCCause::_z_timer:
case GCCause::_z_warmup:
case GCCause::_z_allocation_rate:
case GCCause::_z_allocation_stall:
case GCCause::_z_proactive:
case GCCause::_metadata_GC_threshold:
// Start asynchronous GC
_gc_cycle_port.send_async(cause);
break;
case GCCause::_gc_locker:
// Restart VM operation previously blocked by the GC locker
_gc_locker_port.signal();
break;
default:
// Other causes not supported
fatal("Unsupported GC cause (%s)", GCCause::to_string(cause));
break;
}
}
2.2 消息機制
ZGC使用ZMessagePort類傳遞消息,ZMessagePort內部使用了ZList隊列。
zMessagePort.hpp
class ZMessagePort {
private:
typedef ZMessageRequest<T> Request;
Monitor _monitor;
bool _has_message;
T _message;
uint64_t _seqnum;
ZList<Request> _queue;
public:
ZMessagePort();
void send_sync(T message);
void send_async(T message);
T receive();
void ack();
};
同步消息邏輯如下:
- 首先構造request
- request入隊
- 通知消費者
- 如果是同步消息,需要等待request處理完
zMessagePort.inline.hpp
template <typename T>
inline void ZMessagePort<T>::send_sync(T message) {
Request request;
{
// Enqueue message
MonitorLockerEx ml(&_monitor, Monitor::_no_safepoint_check_flag);
request.initialize(message, _seqnum);
_queue.insert_last(&request);
ml.notify();
}
// Wait for completion
request.wait();
{
// Guard deletion of underlying semaphore. This is a workaround for a
// bug in sem_post() in glibc < 2.21, where it's not safe to destroy
// the semaphore immediately after returning from sem_wait(). The
// reason is that sem_post() can touch the semaphore after a waiting
// thread have returned from sem_wait(). To avoid this race we are
// forcing the waiting thread to acquire/release the lock held by the
// posting thread. https://sourceware.org/bugzilla/show_bug.cgi?id=12674
MonitorLockerEx ml(&_monitor, Monitor::_no_safepoint_check_flag);
}
}
消息消費者負責消費隊列中的消息,如果是異步消息,則直接讀取類變量_message。
zMessagePort.inline.hpp
template <typename T>
inline T ZMessagePort<T>::receive() {
MonitorLockerEx ml(&_monitor, Monitor::_no_safepoint_check_flag);
// Wait for message
while (!_has_message && _queue.is_empty()) {
ml.wait(Monitor::_no_safepoint_check_flag);
}
// Increment request sequence number
_seqnum++;
if (!_has_message) {
// Message available in the queue
_message = _queue.first()->message();
_has_message = true;
}
return _message;
}
2.3 開始gc cycle
ZDriver啓動一個線程,死循環判斷是否應該啓動gc cycle
- start_gc_cycle調用ZMessagePort的receive方法等待啓動請求
- 調用run_gc_cycle方法,執行GC
- 執行GC後,調用end_gc_cycle,ACK啓動請求
zDriver.cpp
void ZDriver::run_service() {
// Main loop
while (!should_terminate()) {
const GCCause::Cause cause = start_gc_cycle();
if (cause != GCCause::_no_gc) {
run_gc_cycle(cause);
end_gc_cycle();
}
}
}
GCCause::Cause ZDriver::start_gc_cycle() {
// Wait for GC request
return _gc_cycle_port.receive();
}
void ZDriver::end_gc_cycle() {
// Notify GC cycle completed
_gc_cycle_port.ack();
// Check for out of memory condition
ZHeap::heap()->check_out_of_memory();
}
run_gc_cycle中,順序執行GC的11個子階段。
zDriver.cpp
void ZDriver::run_gc_cycle(GCCause::Cause cause) {
ZDriverCycleScope scope(cause);
// Phase 1: Pause Mark Start
{
ZMarkStartClosure cl;
vm_operation(&cl);
}
// Phase 2: Concurrent Mark
{
ZStatTimer timer(ZPhaseConcurrentMark);
ZHeap::heap()->mark(true /* initial */);
}
// Phase 3: Pause Mark End
{
ZMarkEndClosure cl;
while (!vm_operation(&cl)) {
// Phase 3.5: Concurrent Mark Continue
ZStatTimer timer(ZPhaseConcurrentMarkContinue);
ZHeap::heap()->mark(false /* initial */);
}
}
// Phase 4: Concurrent Process Non-Strong References
{
ZStatTimer timer(ZPhaseConcurrentProcessNonStrongReferences);
ZHeap::heap()->process_non_strong_references();
}
// Phase 5: Concurrent Reset Relocation Set
{
ZStatTimer timer(ZPhaseConcurrentResetRelocationSet);
ZHeap::heap()->reset_relocation_set();
}
// Phase 6: Concurrent Destroy Detached Pages
{
ZStatTimer timer(ZPhaseConcurrentDestroyDetachedPages);
ZHeap::heap()->destroy_detached_pages();
}
// Phase 7: Pause Verify
if (VerifyBeforeGC || VerifyDuringGC || VerifyAfterGC) {
ZVerifyClosure cl;
vm_operation(&cl);
}
// Phase 8: Concurrent Select Relocation Set
{
ZStatTimer timer(ZPhaseConcurrentSelectRelocationSet);
ZHeap::heap()->select_relocation_set();
}
// Phase 9: Concurrent Prepare Relocation Set
{
ZStatTimer timer(ZPhaseConcurrentPrepareRelocationSet);
ZHeap::heap()->prepare_relocation_set();
}
// Phase 10: Pause Relocate Start
{
ZRelocateStartClosure cl;
vm_operation(&cl);
}
// Phase 11: Concurrent Relocate
{
ZStatTimer timer(ZPhaseConcurrentRelocated);
ZHeap::heap()->relocate();
}
}