0
点赞
收藏
分享

微信扫一扫

Golang环境变量之GODEBUG

向上的萝卜白菜 2021-09-30 阅读 94

GODEBUG 是 golang 中一个控制runtime调度变量的变量,其值为一个用逗号隔开的 name=val对列表,常见有以下几个命名变量。

allocfreetrace

设置allocfreetrace = 1会导致对每个分配进行概要分析,并在每个对象的分配上打印堆栈跟踪并释放它们。

clobberfree

设置 clobberfree=1会使垃圾回收器在释放对象的时候,对象里的内存内容可能是错误的。

cgocheck

cgo相关。

设置 cgocheck=0 将禁用当包使用cgo非法传递给go指针到非go代码的检查。如果值为1(默认值)会启用检测,但可能会丢失有一些错误。如果设置为2的话,则不会丢失错误。但会使程序变慢。

efence

设置 efence=1会使回收器运行在一个模式。每个对象都在一个唯一的页和地址,且永远也不会被回收。

gccheckmark

GC相关。

设置 gccheckmark=1 启用验证垃圾回收器的并发标记,通过在STW时第二个标记阶段来实现,如果在第二阶段的时候,找到一个可达对象,但未找到并发标记,则GC会发生Panic。

gcpacertrace

GC相关。

设置 gcpacertrace=1可使gc打印关于并发 pacer 内部状态的信息

gcshrinkstackoff

GC相关。

设置 gcshrinkstackoff=1可禁止goroutine移动到小stack。这种模式下,goroutine stack只能增长。简单来说就是一个关闭收缩的开关。

gcstoptheworld

GC 相关。

设置 gcstoptheworld=1可禁止并发垃圾回收,使每个gc就是一个事件。设置 gcstoptheworld=2 会在垃圾回收后进行并发扫描。

gctrace

GC 相关。

设置 gctrade=1 会使垃圾回收器在每次GC打印单行标准错误,汇总收集的内存量和暂停的时间。格式可能会更换。

如果输出行以(forced)结尾,则说明此GC是通过调用 runtime.GC 来触发的。这个调试变量非常常见。如 GODEBUG=gotrace=1 go run main.go

➜  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

字段解释

垃圾回收信息

1 :表示第一次执行
@0.115s :表示当前程序执行的总时间,从程序开始时计时。
0% :垃圾回收时间占用的百分比
0.067+0.92+0.003 ms clock :垃圾回收的时间,分别为STW(stop-the-world)清扫时间, 并发标记和扫描的时间,STW标记的时间
0.26+0.41/0.78/0.011+0.015 ms cpu :垃圾回收占用cpu时间
4->4->0 MB :堆的大小,三个数字分别表示GC开始前堆的大小,GC后堆的大小 和 当前存活堆的大小
5 MB goal :整体堆的大小
4 P :使用的处理器数量

在gc后的内存回收到系统时,会有一些概要信息,如
scvg0: inuse: 426, idle: 0, sys: 427, released: 0, consumed: 427 (MB)

426 :使用多少M内存
0 :剩下要清除的内存
427 :系统映射的内存
0 :释放的系统内存
427 :申请的系统内存

madvdontneed

设置 madvdontneed=1 将会在Linux上当内存返回内核时使用 MADV_DONTNEED 代替 MADV_FREE

memprofilerate

设置memprofilerate=X 将会更新 runtime.MemProfileRate 的值。如果设置为 0 将禁用内存性能分析。有关默认值,请参考 MemProfileRate 的描述。

invalidptr

// TODO

sbrk

设置sbrk=1会使用一个碎片回收器代替内存分配器和垃圾回收器。它从操作系统获取内存,并且永远也不会回收任何内存。

scavenge

设置 scavenge=1 将启用heap scavenger的调试模式。

scavtrace

//TODO

scheddetail

调度相关。

设置为schedtrace=Xscheddetail=1 会使调度器每 X 毫秒打印详细的多行信息,调度器的描述状态,处理器、线程和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()
}

输出结果

➜  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

输出的内容比较的多,每行的信息属于谁只要看首列字母即可,如果是G开头的话,则是表示的是 GoRoutine, G0、G1、GN后面的数字表示是G的编号,是唯一的。如果是P的话,则就是处理器相关的信息,当然M就是线程了,每种资源都有多个,他们之间状态是来回变化的,由于阻塞或调度的原因。SCHED 则表示当前的调度信息。

建议看一下每行相关的字段意义,这样有助于理解 runtime。如 P 行输出行里的字段 statusschedticksyscalltickmrunqsize 全是 P 结构体的字段。

P相关字段

  • status 当前P的状态,参考这里
  • schedtick 调度次数
  • syscalltick P系统调用次数
  • m 当前P绑定的M
  • runqsize P本地的运行队列,存放的全是待执行的Goroutine
  • gfreecnt 可用的G(状态Gdead)

M相关字段

  • p 当前m绑定的P
  • curg 当前绑定的G
  • mallocing 分配内存状态
  • throwing 是否抛出异常
  • preemptoff 如果非空,则保持curg 运行在当前m。
  • locks
  • dying 死亡?
  • spinning 是否自旋
  • blocked 当前阻塞
  • lockedg 当前锁定的g

G相关字段

  • status 当前G 的状态,参考这里
  • m 当前G分配到的M, -1表示未分配
  • lockedm 当前G锁定的m

schedtrace

调度相关。

设置 schedtrade=X 会使调度器每X毫秒打印一行标准错误,汇总调度器的状态。

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()
}

输出结果

➜  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:每一行都表示一次调度器的调试信息,后面提示的毫秒数表示启动到现在的运行时间,输出的时间间隔受 schedtrace 的值影响。
  • gomaxprocs:当前的 CPU 核心数(GOMAXPROCS 的当前值)。
  • idleprocs:空闲的处理器数量,后面的数字表示当前的空闲数量。
  • threads:OS 线程数量,后面的数字表示当前正在运行的线程数量。
  • spinningthreads:自旋状态的 OS 线程数量。
  • idlethreads:空闲的线程数量。
  • runqueue:全局队列中 Goroutine 数量,而后面的 [3 1 3 0] 则分别代表这 4 个 P 的本地队列正在运行的 Goroutine 数量。

如果想打印更详情的信息,可能可以与 scheddetail 一起使用,如GODEBUG=schedtrace=1000,scheddetail=1 go run main.go ,会打印出每个G/P/M之间的切换状态详情。

推荐使用pprof查看,目前这种方式不是太友好。

tracebackancestors

//TODO

asyncpreemptoff

异步抢占

以上几个调试变量,经常使用的有gotraceschedtracescheddetail,其中gotrace主要是观察gc的情况,schedtrace 和 scheddetail 主要是观察调度的情况。

另外netnet/httpcrypto/tls 包也引用了GODEUBG中的调度变量,具体参考包的文档。

原文:https://blog.haohtml.com/archives/21778

参考

举报

相关推荐

0 条评论