GODEBUG 是 golang 中一個控制runtime調(diào)度變量的變量,其值為一個用逗號隔開的 name=val對列表,常見有以下幾個命名變量。
allocfreetrace
設置allocfreetrace = 1會導致對每個分配進行概要分析,并在每個對象的分配上打印堆棧跟蹤并釋放它們。
clobberfree
設置 clobberfree=1會使垃圾回收器在釋放對象的時候,對象里的內(nèi)存內(nèi)容可能是錯誤的。
cgocheck
cgo相關。
設置 cgocheck=0 將禁用當包使用cgo非法傳遞給go指針到非go代碼的檢查。如果值為1(默認值)會啟用檢測,但可能會丟失有一些錯誤。如果設置為2的話,則不會丟失錯誤。但會使程序變慢。
efence
設置 efence=1會使回收器運行在一個模式。每個對象都在一個唯一的頁和地址,且永遠也不會被回收。
gccheckmark
GC相關。
設置 gccheckmark=1 啟用驗證垃圾回收器的并發(fā)標記,通過在STW時第二個標記階段來實現(xiàn),如果在第二階段的時候,找到一個可達對象,但未找到并發(fā)標記,則GC會發(fā)生Panic。
gcpacertrace
GC相關。
設置 gcpacertrace=1可使gc打印關于并發(fā) pacer 內(nèi)部狀態(tài)的信息
gcshrinkstackoff
GC相關。
設置 gcshrinkstackoff=1可禁止goroutine移動到小stack。這種模式下,goroutine stack只能增長。簡單來說就是一個關閉收縮的開關。
gcstoptheworld
GC 相關。
設置 gcstoptheworld=1可禁止并發(fā)垃圾回收,使每個gc就是一個事件。設置 gcstoptheworld=2 會在垃圾回收后進行并發(fā)掃描。
gctrace
GC 相關。
設置 gctrade=1 會使垃圾回收器在每次GC打印單行標準錯誤,匯總收集的內(nèi)存量和暫停的時間。格式可能會更換。
如果輸出行以(forced)結(jié)尾,則說明此GC是通過調(diào)用 runtime.GC 來觸發(fā)的。這個調(diào)試變量非常常見。如 GODEBUG=gotrace=1 go run main.go
輸出字段意義:
gc # @#s #%: #+#+# ms clock, #+#/#/#+# ms cpu, #->#-># MB, # MB goal, # P
where the fields are as follows:
gc # the GC number, incremented at each GC
@#s time in seconds since program start
#% percentage of time spent in GC since program start
#+...+# wall-clock/CPU times for the phases of the GC
#->#-># MB heap size at GC start, at GC end, and live heap
# MB goal goal heap size
# P number of processors used
The phases are stop-the-world (STW) sweep termination, concurrent
mark and scan, and STW mark termination. The CPU times
for mark/scan are broken down in to assist time (GC performed in
line with allocation), background GC time, and idle GC time.
If the line ends with "(forced)", this GC was forced by a
runtime.GC() call.
? gotest GODEBUG=gctrace=1 go run main.go
gc 1 @0.115s 0%: 0.067+0.92+0.003 ms clock, 0.26+0.41/0.78/0.011+0.015 ms cpu, 4->4->0 MB, 5 MB goal, 4 P
gc 2 @0.194s 0%: 0.056+1.5+0.003 ms clock, 0.22+1.3/0.27/1.6+0.012 ms cpu, 4->4->0 MB, 5 MB goal, 4 P
gc 3 @0.303s 0%: 0.24+1.5+0.004 ms clock, 0.98+0.20/0.32/1.5+0.016 ms cpu, 4->4->0 MB, 5 MB goal, 4 P
gc 4 @0.344s 0%: 0.12+0.45+0.003 ms clock, 0.51+0/0.41/0.81+0.012 ms cpu, 4->4->0 MB, 5 MB goal, 4 P
字段解釋
垃圾回收信息
gc 1 @0.115s 0%: 0.018+1.3+0.076 ms clock, 0.054+0.35/1.0/3.0+0.23 ms cpu, 4->4->3 MB, 5 MB goal, 4 P`。
1 :表示第一次執(zhí)行
@0.115s :表示當前程序執(zhí)行的總時間,從程序開始時計時。
0% :垃圾回收時間占用的百分比
0.067+0.92+0.003 ms clock :垃圾回收的時間,分別為STW(stop-the-world)清掃時間, 并發(fā)標記和掃描的時間,STW標記的時間
0.26+0.41/0.78/0.011+0.015 ms cpu :垃圾回收占用cpu時間
4->4->0 MB :堆的大小,三個數(shù)字分別表示GC開始前堆的大小,GC后堆的大小 和 當前存活堆的大小
5 MB goal :整體堆的大小
4 P :使用的處理器數(shù)量
在gc后的內(nèi)存回收到系統(tǒng)時,會有一些概要信息,如
scvg0: inuse: 426, idle: 0, sys: 427, released: 0, consumed: 427 (MB)
426 :使用多少M內(nèi)存
0 :剩下要清除的內(nèi)存
427 :系統(tǒng)映射的內(nèi)存
0 :釋放的系統(tǒng)內(nèi)存
427 :申請的系統(tǒng)內(nèi)存
madvdontneed
設置 madvdontneed=1 將會在Linux上當內(nèi)存返回內(nèi)核時使用 MADV_DONTNEED 代替 MADV_FREE。
memprofilerate
設置memprofilerate=X 將會更新 runtime.MemProfileRate 的值。如果設置為 0 將禁用內(nèi)存性能分析。有關默認值,請參考 MemProfileRate 的描述。
invalidptr
// TODO
sbrk
設置sbrk=1會使用一個碎片回收器代替內(nèi)存分配器和垃圾回收器。它從操作系統(tǒng)獲取內(nèi)存,并且永遠也不會回收任何內(nèi)存。
scavenge
設置 scavenge=1 將啟用heap scavenger的調(diào)試模式。
scavtrace
//TODO
scheddetail
調(diào)度相關。
設置為schedtrace=X 和 scheddetail=1 會使調(diào)度器每 X 毫秒打印詳細的多行信息,調(diào)度器的描述狀態(tài),處理器、線程和goroutines。
package main
import "sync"
func main() {
wg := sync.WaitGroup{}
wg.Add(10)
for i := 0; i < 10; i++ {
go func(wg *sync.WaitGroup) {
var counter int
for i := 0; i < 1e10; i++ {
counter++
}
wg.Done()
}(&wg)
}
wg.Wait()
}
輸出結(jié)果
? gotest GODEBUG=schedtrace=1000,scheddetail=1000 go run main.go
SCHED 0ms: gomaxprocs=4 idleprocs=2 threads=6 spinningthreads=1 idlethreads=2 runqueue=0 gcwaiting=0 nmidlelocked=0 stopwait=0 sysmonwait=0
P0: status=1 schedtick=1 syscalltick=0 m=0 runqsize=0 gfreecnt=0 timerslen=0
P1: status=0 schedtick=2 syscalltick=0 m=-1 runqsize=0 gfreecnt=0 timerslen=0
P2: status=1 schedtick=0 syscalltick=0 m=5 runqsize=0 gfreecnt=0 timerslen=0
P3: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0 timerslen=0
M5: p=2 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=true blocked=false lockedg=-1
M4: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1
M3: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1
M2: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=2 dying=0 spinning=false blocked=false lockedg=-1
M1: p=-1 curg=17 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=false lockedg=17
M0: p=0 curg=1 mallocing=0 throwing=0 preemptoff= locks=3 dying=0 spinning=false blocked=false lockedg=1
G1: status=2(chan receive) m=0 lockedm=0
G17: status=6() m=1 lockedm=1
G2: status=4(force gc (idle)) m=-1 lockedm=-1
G3: status=4(GC sweep wait) m=-1 lockedm=-1
G4: status=4(GC scavenge wait) m=-1 lockedm=-1
SCHED 0ms: gomaxprocs=4 idleprocs=1 threads=5 spinningthreads=1 idlethreads=0 runqueue=0 gcwaiting=0 nmidlelocked=0 stopwait=0 sysmonwait=0
P0: status=1 schedtick=0 syscalltick=0 m=4 runqsize=1 gfreecnt=0 timerslen=0
P1: status=1 schedtick=0 syscalltick=0 m=2 runqsize=0 gfreecnt=0 timerslen=0
P2: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0 timerslen=0
P3: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0 timerslen=0
M4: p=0 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1
輸出的內(nèi)容比較的多,每行的信息屬于誰只要看首列字母即可,如果是G開頭的話,則是表示的是 GoRoutine, G0、G1、GN后面的數(shù)字表示是G的編號,是唯一的。如果是P的話,則就是處理器相關的信息,當然M就是線程了,每種資源都有多個,他們之間狀態(tài)是來回變化的,由于阻塞或調(diào)度的原因。SCHED 則表示當前的調(diào)度信息。
建議看一下每行相關的字段意義,這樣有助于理解 runtime。如 P 行輸出行里的字段 status、schedtick、syscalltick、m 和 runqsize 全是 P 結(jié)構(gòu)體的字段。
P相關字段
-
status當前P的狀態(tài),參考這里 -
schedtick調(diào)度次數(shù) -
syscalltickP系統(tǒng)調(diào)用次數(shù) -
m當前P綁定的M -
runqsizeP本地的運行隊列,存放的全是待執(zhí)行的Goroutine -
gfreecnt可用的G(狀態(tài)Gdead)
M相關字段
-
p當前m綁定的P -
curg當前綁定的G -
mallocing分配內(nèi)存狀態(tài) -
throwing是否拋出異常 -
preemptoff如果非空,則保持curg 運行在當前m。 -
locks鎖 -
dying死亡? -
spinning是否自旋 -
blocked當前阻塞 -
lockedg當前鎖定的g
G相關字段
-
status當前G 的狀態(tài),參考這里 -
m當前G分配到的M, -1表示未分配 -
lockedm當前G鎖定的m
schedtrace
調(diào)度相關。
設置 schedtrade=X 會使調(diào)度器每X毫秒打印一行標準錯誤,匯總調(diào)度器的狀態(tài)。
package main
import "sync"
func main() {
wg := sync.WaitGroup{}
wg.Add(10)
for i := 0; i < 10; i++ {
go func(wg *sync.WaitGroup) {
var counter int
for i := 0; i < 1e10; i++ {
counter++
}
wg.Done()
}(&wg)
}
wg.Wait()
}
輸出結(jié)果
? gotest GODEBUG=schedtrace=1000 go run main.go
SCHED 0ms: gomaxprocs=4 idleprocs=2 threads=4 spinningthreads=1 idlethreads=0 runqueue=0 [1 0 0 0]
SCHED 1ms: gomaxprocs=4 idleprocs=2 threads=7 spinningthreads=1 idlethreads=2 runqueue=0 [0 0 0 0]
SCHED 3ms: gomaxprocs=4 idleprocs=2 threads=7 spinningthreads=1 idlethreads=2 runqueue=0 [0 0 0 0]
SCHED 10ms: gomaxprocs=4 idleprocs=1 threads=7 spinningthreads=1 idlethreads=1 runqueue=0 [7 7 0 0]
SCHED 15ms: gomaxprocs=4 idleprocs=0 threads=7 spinningthreads=1 idlethreads=0 runqueue=0 [3 1 3 0]
SCHED 18ms: gomaxprocs=4 idleprocs=0 threads=7 spinningthreads=0 idlethreads=0 runqueue=0 [1 1 3 1]
SCHED 22ms: gomaxprocs=4 idleprocs=0 threads=8 spinningthreads=0 idlethreads=0 runqueue=1 [1 1 3 1]
SCHED 24ms: gomaxprocs=4 idleprocs=0 threads=8 spinningthreads=0 idlethreads=1 runqueue=0 [0 0 0 0]
SCHED 25ms: gomaxprocs=4 idleprocs=0 threads=8 spinningthreads=0 idlethreads=1 runqueue=0 [0 0 0 0]
SCHED 29ms: gomaxprocs=4 idleprocs=0 threads=8 spinningthreads=1 idlethreads=0 runqueue=0 [0 6 0 0]
SCHED 30ms: gomaxprocs=4 idleprocs=1 threads=8 spinningthreads=1 idlethreads=1 runqueue=0 [0 0 0 0]
......
我們這時設置的是 schedtrace=1000,即1秒輸出一次,
-
SCHED:每一行都表示一次調(diào)度器的調(diào)試信息,后面提示的毫秒數(shù)表示啟動到現(xiàn)在的運行時間,輸出的時間間隔受schedtrace的值影響。 -
gomaxprocs:當前的 CPU 核心數(shù)(GOMAXPROCS 的當前值)。 -
idleprocs:空閑的處理器數(shù)量,后面的數(shù)字表示當前的空閑數(shù)量。 -
threads:OS線程數(shù)量,后面的數(shù)字表示當前正在運行的線程數(shù)量。 -
spinningthreads:自旋狀態(tài)的 OS 線程數(shù)量。 -
idlethreads:空閑的線程數(shù)量。 -
runqueue:全局隊列中 Goroutine 數(shù)量,而后面的 [3 1 3 0] 則分別代表這 4 個 P 的本地隊列正在運行的 Goroutine 數(shù)量。
如果想打印更詳情的信息,可能可以與 scheddetail 一起使用,如GODEBUG=schedtrace=1000,scheddetail=1 go run main.go ,會打印出每個G/P/M之間的切換狀態(tài)詳情。
推薦使用pprof查看,目前這種方式不是太友好。
tracebackancestors
//TODO
asyncpreemptoff
異步搶占
以上幾個調(diào)試變量,經(jīng)常使用的有gotrace、schedtrace 和 scheddetail,其中g(shù)otrace主要是觀察gc的情況,schedtrace 和 scheddetail 主要是觀察調(diào)度的情況。
另外net、net/http和 crypto/tls 包也引用了GODEUBG中的調(diào)度變量,具體參考包的文檔。
原文:https://blog.haohtml.com/archives/21778