會誘發 Goroutine 掛起的 27 個原因
本文轉載自微信公眾號「腦子進煎魚了」,作者陳煎魚。轉載本文請聯系腦子進煎魚了公眾號。
大家好,我是煎魚。
上個月面向讀者的提問,我們針對 goroutine 泄露中都會看到的大頭 runtime.gopark 函數進行了學習和了解,輸出了 《Goroutine 一泄露就看到他,這是個什么?》。
有小伙伴提到,雖然我們知道了 runtime.gopark 函數的緣起和內在,但其實沒有提到 runtime.gopark 的誘發原因,畢竟他會導致 Goroutine 掛起,這是我們日常編碼中需要關注的。
為此我整理了一下筆記,今天這篇文章就和大家一起圍觀 gopark 的 27 個誘發原因。為了方便閱讀,我們會根據分類進行說明。
第一部分
標識 | 含義 |
---|---|
waitReasonZero | 無 |
waitReasonGCAssistMarking | GC assist marking |
waitReasonIOWait | IO wait |
- waitReasonZero:無正式解釋,從使用情況來看。主要在 sleep 和 lock 的 2 個場景中使用。
- waitReasonGCAssistMarking:GC 輔助標記階段會使得阻塞等待。
- waitReasonIOWait:IO 阻塞等待時,例如:網絡請求等。
第二部分
標識 | 含義 |
---|---|
waitReasonChanReceiveNilChan | chan receive (nil chan) |
waitReasonChanSendNilChan | chan send (nil chan) |
- waitReasonChanReceiveNilChan:對未初始化的 channel 進行讀操作。
- waitReasonChanSendNilChan:對未初始化的 channel 進行寫操作。
第三部分
標識 | 含義 |
---|---|
waitReasonDumpingHeap | dumping heap |
waitReasonGarbageCollection | garbage collection |
waitReasonGarbageCollectionScan | garbage collection scan |
- waitReasonDumpingHeap:對 Go Heap 堆 dump 時,這個的使用場景僅在 runtime.debug 時,也就是常見的 pprof 這一類采集時阻塞。
- waitReasonGarbageCollection:在垃圾回收時,主要場景是 GC 標記終止(GC Mark Termination)階段時觸發。
- waitReasonGarbageCollectionScan:在垃圾回收掃描時,主要場景是 GC 標記(GC Mark)掃描 Root 階段時觸發。
第四部分
標識 | 含義 |
---|---|
waitReasonPanicWait | panicwait |
waitReasonSelect | select |
waitReasonSelectNoCases | select (no cases) |
- waitReasonPanicWait:在 main goroutine 發生 panic 時,會觸發。
- waitReasonSelect:在調用關鍵字 select 時會觸發。
- waitReasonSelectNoCases:在調用關鍵字 select 時,若一個 case 都沒有,會直接觸發。
第五部分
標識 | 含義 |
---|---|
waitReasonGCAssistWait | GC assist wait |
waitReasonGCSweepWait | GC sweep wait |
waitReasonGCScavengeWait | GC scavenge wait |
- waitReasonGCAssistWait:GC 輔助標記階段中的結束行為,會觸發。
- waitReasonGCSweepWait:GC 清掃階段中的結束行為,會觸發。
- waitReasonGCScavengeWait:GC scavenge 階段的結束行為,會觸發。GC Scavenge 主要是新空間的垃圾回收,是一種經常運行、快速的 GC,負責從新空間中清理較小的對象。
第六部分
標識 | 含義 |
---|---|
waitReasonChanReceive | chan receive |
waitReasonChanSend | chan send |
waitReasonFinalizerWait | finalizer wait |
- waitReasonChanReceive:在 channel 進行讀操作,會觸發。
- waitReasonChanSend:在 channel 進行寫操作,會觸發。
- waitReasonFinalizerWait:在 finalizer 結束的階段,會觸發。在 Go 程序中,可以通過調用 runtime.SetFinalizer 函數來為一個對象設置一個終結者函數。這個行為對應著結束階段造成的回收。
第七部分
標識 | 含義 |
---|---|
waitReasonForceGCIdle | force gc (idle) |
waitReasonSemacquire | semacquire |
waitReasonSleep | sleep |
- waitReasonForceGCIdle:強制 GC(空閑時間)結束時,會觸發。
- waitReasonSemacquire:信號量處理結束時,會觸發。
- waitReasonSleep:經典的 sleep 行為,會觸發。
第八部分
標識 | 含義 |
---|---|
waitReasonSyncCondWait | sync.Cond.Wait |
waitReasonTimerGoroutineIdle | timer goroutine (idle) |
waitReasonTraceReaderBlocked | trace reader (blocked) |
- waitReasonSyncCondWait:結合 sync.Cond 用法能知道,是在調用 sync.Wait 方法時所觸發。
- waitReasonTimerGoroutineIdle:與 Timer 相關,在沒有定時器需要執行任務時,會觸發。
- waitReasonTraceReaderBlocked:與 Trace 相關,ReadTrace會返回二進制跟蹤數據,將會阻塞直到數據可用。
第九部分
標識 | 含義 |
---|---|
waitReasonWaitForGCCycle | wait for GC cycle |
waitReasonGCWorkerIdle | GC worker (idle) |
waitReasonPreempted | preempted |
waitReasonDebugCall | debug call |
- waitReasonWaitForGCCycle:等待 GC 周期,會休眠造成阻塞。
- waitReasonGCWorkerIdle:GC Worker 空閑時,會休眠造成阻塞。
- waitReasonPreempted:發生循環調用搶占時,會會休眠等待調度。
- waitReasonDebugCall:調用 GODEBUG 時,會觸發。
總結
今天這篇文章是對開頭 runtime.gopark 函數的詳解文章的一個補充,我們能夠對此了解到其誘發的因素。
主要場景為:
- 通道(Channel)。
- 垃圾回收(GC)。
- 休眠(Sleep)。
- 鎖等待(Lock)。
- 搶占(Preempted)。
- IO 阻塞(IO Wait)
- 其他,例如:panic、finalizer、select 等。
我們可以根據這些特性,去拆解可能會造成阻塞的原因。其實也就沒必要記了,他們會導致阻塞肯定是由于存在影響控制流的因素,才會導致 gopark 的調用。