go gc 分析

1 先翻译一下runtime 文档中,关于gc的内容(里面涉及GC日志格式)

···
原文: https://golang.org/pkg/runtime/
翻译参考: https://colobu.com/2016/07/04/dive-into-go-11/#pprof

···

allocfreetrace: 设置 allocfreetrace=1 会监控每次分配,但因每次分配和释放的栈信息(stack trace)

cgocheck: 设置 cgocheck=0 禁用所有cgo检查将Go指针传递给非Go代码是否正确。
cgocheck=1 (缺省值) 轻量级检查。cgocheck=2 重量级检查。

efence: 设置 efence=1 导致分配器 allocator将每个对象分配在一个唯一的页page上,地址不重用。

gccheckmark: 设置 gccheckmark=1 允许垃圾回收器执行并发mark阶段的校验。会导致Stop The World。

gcpacertrace: 设置 gcpacertrace=1 会让来几回收器打印出concurrent pacer的内部状态。

gcshrinkstackoff: 设置 gcshrinkstackoff=1 则禁止将 goroutines 的栈缩小为更小栈。

gcstackbarrieroff: 设置 gcstackbarrieroff=1 禁用stack barriers,会影响垃圾回收器的重复搜索栈的功能。

gcstackbarrierall: 设置 gcstackbarrierall=1 会为每个栈帧安装一 stack barriers。

gcstoptheworld: 设置 gcstoptheworld=1 则禁用并发垃圾回收,每次回收都会触发STW。设置gcstoptheworld=2则禁用垃圾回收后的concurrent sweeping。

gctrace: 设置 gctrace=1导致每次垃圾回收器触发一行日志,包含内存回收的概要信息和暂停的时间。设置gctrace=2起同样的效果,but also repeats each collection。格式如下:

gc # @#s #%: #+#+# ms clock, #+#/#/#+# ms cpu, #->#-># MB, # MB goal, # P

where the fields are as follows:
gc # GC id,每次GC加一
@#s 程序启动后的时间,单位秒
#% 程序启动后GC所用的时间比
#+...+# 此次GC所用的wall-clock/CPU时间
#->#-># MB GC开始时的堆大小, GC结束时的堆大小, 活着的(live)堆大小
# MB goal 总的堆大小
# P CPU使用数
垃圾回收分为下面的几个阶段:stop-the-world (STW) sweep termination, concurrent
mark and scan, and STW mark termination。 mark/scan的CPU时间又分为 assist time (GC performed in
line with allocation), background GC time, and idle GC time。

垃圾回收的四个阶段:
Sweep Termination: 对未清扫的span进行清扫, 只有上一轮的GC的清扫工作完成才可以开始新一轮的GC
Mark: 扫描所有根对象, 和根对象可以到达的所有对象, 标记它们不被回收
Mark Termination: 完成标记工作, 重新扫描部分根对象(要求STW)
Sweep: 按标记结果清扫span

如果日志后以"(forced)"结尾,则GC通过runtime.GC()调用执行,此时所有的阶段都是STW.

memprofilerate: 设置 memprofilerate=X 会更新runtime.MemProfileRate的值。0则禁用这个profie。

invalidptr: 默认设为invalidptr=1, 如果指针被赋予一个无效值,会引起程序的崩溃,设置该值为0,会停止该检查,
0只能临时用于查找bug,真正的解决方法是不要把整数类型的值存在指针变量里面。

sbrk: 设置 sbrk=1 会使用实验性的实现替换memory allocator 和 garbage collector。

scavenge: scavenge=1 允许heap scavenger的debug模式。

scheddetail: 设置 schedtrace=X 和 scheddetail=1 会导致goroutine调度器每个X毫秒输出多行调度信息。

schedtrace: 设置 schedtrace=X导致调度器每个X秒输出一行调度器的概要信息。

2 GC 触发时机

gcTriggerHeap: 当前分配的内存达到一定值就触发GC
gcTriggerTime: 当一定时间没有执行过GC就触发GC
gcTriggerCycle: 要求启动新一轮的GC, 已启动则跳过, 手动触发GC的runtime.GC()会使用这个条件

3 gc 具体的过程

3.1 根对象

在GC的标记阶段首先需要标记的就是"根对象", 从根对象开始可到达的所有对象都会被认为是存活的.
根对象包含了全局变量, 各个G的栈上的变量等, GC会先扫描根对象然后再扫描根对象可到达的所有对象.

3.2 三色标记过程

之前自己整理的 https://www.jianshu.com/p/ebf03d9605d0

4 gc 优化案例

4.1 减少分配对象数量(这样就减少了扫描时间)

链接:https://www.zhihu.com/question/21615032/answer/18781477

4.2 一个检测G 长时间占用CPU时间片,导致GC hang住的排查工具

https://github.com/zhanglvmeng/go-tool-trace-greediest-goroutines

4.3 【已验证】io.copybuffer 没传buffer进去,底层自动创建了多个buffer对象,造成内存泄漏,频繁gc。

https://my.oschina.net/u/2950272/blog/1788299

4.4 使用array 替代 map, 减少扫描时间

https://studygolang.com/articles/1720

4.5 多个不同维度的分析

http://guileen.github.io/2016/06/15/how-did-i-optimize-golang-gc/

4.6 减少GC的一些思路

http://www.philo.top/2015/05/29/golangProfilingAndGC2/

4.7 GC排查的步骤

http://xiaorui.cc/2016/03/20/golang%E4%BD%BF%E7%94%A8pprof%E7%9B%91%E6%8E%A7%E6%80%A7%E8%83%BD%E5%8F%8Agc%E8%B0%83%E4%BC%98/

4.8 string 以及[]byte 的GC问题

https://www.520mwx.com/view/35045

5 gc 查看工具

gcvis https://github.com/davecheney/gcvis
参考自 https://colobu.com/2016/07/04/dive-into-go-11/#pprof

6 GC 监控

对于线上GC的监控,基本上读取runtime.MemStats结构中的内容,然后存储到时序数据库中。具体有如下两种获取方式:

// 方式1
memStats := &runtime.MemStats{}
runtime.ReadMemStats(memStats)

// 方式2 json格式
expvar.Get("memstats").String()

7 scavenger

到目前为止,gctrace给出的最有用的信息就是 the heap scavenger的输出.

scvg143: inuse: 8, idle: 104, sys: 113, released: 104, consumed: 8 (MB)

scvg143 表示第143次输出。其他字段,见下图。

scvg.png

图片来源于https://colobu.com/2016/07/04/dive-into-go-11/#pprof

scavenger 的工作就是周期性地打扫heap中无用的操作系统内存分页, 它会向操作系统发出建义,请操作系统回收无用内存页,

当然并不能强迫操作系统立刻就去做回收处理,操作系统可以忽略此建义,或是延迟回收,比如直到可分配的空闲内存不够的时候。

scavenger输出的信息是我们了解go程序虚拟内存空间使用情况的最好方式, 当然你也可以通过其它工具,如free, top来获到这些信息,
不过你应用信任scavenger.

8 参考文献

讲的很好,概述性:https://xenojoshua.com/2019/03/golang-memory/#31-%E7%89%88%E6%9C%AC%E5%8E%86%E5%8F%B2--%E6%BC%94%E8%BF%9B

非常详细的文章https://yq.aliyun.com/blog/573819
gc 线上监控: http://kuring.me/post/golang-gc/

scavenger: https://studygolang.com/articles/6346

推荐阅读更多精彩内容