原文地址:用 GODEBUG 看 GChtml
在计算机科学中,垃圾回收(GC)是一种自动管理内存的机制,垃圾回收器会去尝试回收程序再也不使用的对象及其占用的内存。而最先 John McCarthy 在 1959 年左右发明了垃圾回收,以简化 Lisp 中的手动内存管理的机制(来自 wikipedia)。git
手动管理内存挺麻烦,管错或者管漏内存也很糟糕,将会直接致使程序不稳定(持续泄露)甚至直接崩溃。github
硬要说会带来什么问题的话,也就数你们最关注的 Stop The World(STW),STW 代指在执行某个垃圾回收算法的某个阶段时,须要将整个应用程序暂停去处理 GC 相关的工做事项。例如:golang
行为 | 会不会 STW | 为何 |
---|---|---|
标记开始 | 会 | 在开始标记时,准备根对象的扫描,会打开写屏障(Write Barrier) 和 辅助GC(mutator assist),而回收器和应用程序是并发运行的,所以会暂停当前正在运行的全部 Goroutine。 |
并发标记中 | 不会 | 标记阶段,主要目的是标记堆内存中仍在使用的值。 |
标记结束 | 会 | 在完成标记任务后,将从新扫描部分根对象,这时候会禁用写屏障(Write Barrier)和辅助GC(mutator assist),而标记阶段和应用程序是并发运行的,因此在标记阶段可能会有新的对象产生,所以在从新扫描时须要进行 STW。 |
能够经过 GOGC 变量设置初始垃圾收集器的目标百分比值,对比的规则为当新分配的数值与上一次收集后剩余的实时数值的比例达到设置的目标百分比时,就会触发 GC,默认值为 GOGC=100。若是将其设置为 GOGC=off 能够彻底禁用垃圾回收器,要不试试?算法
简单来说就是,GOGC 的值设置的越大,GC 的频率越低,但每次最终所触发到 GC 的堆内存也会更大。c#
版本 | GC 算法 | STW 时间 |
---|---|---|
Go 1.0 | STW(强依赖 tcmalloc) | 百ms到秒级别 |
Go 1.3 | Mark STW, Sweep 并行 | 百ms级别 |
Go 1.5 | 三色标记法, 并发标记清除。同时运行时从 C 和少许汇编,改成 Go 和少许汇编实现 | 10-50ms级别 |
Go 1.6 | 1.5 中一些与并发 GC 不协调的地方更改,集中式的 GC 协调协程,改成状态机实现 | 5ms级别 |
Go 1.7 | GC 时由 mark 栈收缩改成并发,span 对象分配机制由 freelist 改成 bitmap 模式,SSA引入 | ms级别 |
Go 1.8 | 混合写屏障(hybrid write barrier), 消除 re-scanning stack | sub ms |
Go 1.12 | Mark Termination 流程优化 | sub ms, 但几乎减小一半 |
注:资料来源于 @boya 在深圳 Gopher Meetup 的分享。并发
GODEBUG 变量能够控制运行时内的调试变量,参数以逗号分隔,格式为:name=val
。本文着重点在 GC 的观察上,主要涉及 gctrace 参数,咱们经过设置 gctrace=1
后就能够使得垃圾收集器向标准错误流发出 GC 运行信息。post
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() }
$ GODEBUG=gctrace=1 go run main.go gc 1 @0.032s 0%: 0.019+0.45+0.003 ms clock, 0.076+0.22/0.40/0.80+0.012 ms cpu, 4->4->0 MB, 5 MB goal, 4 P gc 2 @0.046s 0%: 0.004+0.40+0.008 ms clock, 0.017+0.32/0.25/0.81+0.034 ms cpu, 4->4->0 MB, 5 MB goal, 4 P gc 3 @0.063s 0%: 0.004+0.40+0.008 ms clock, 0.018+0.056/0.32/0.64+0.033 ms cpu, 4->4->0 MB, 5 MB goal, 4 P gc 4 @0.080s 0%: 0.004+0.45+0.016 ms clock, 0.018+0.15/0.34/0.77+0.065 ms cpu, 4->4->1 MB, 5 MB goal, 4 P gc 5 @0.095s 0%: 0.015+0.87+0.005 ms clock, 0.061+0.27/0.74/1.8+0.023 ms cpu, 4->4->1 MB, 5 MB goal, 4 P gc 6 @0.113s 0%: 0.014+0.69+0.002 ms clock, 0.056+0.23/0.48/1.4+0.011 ms cpu, 4->4->1 MB, 5 MB goal, 4 P gc 7 @0.140s 1%: 0.031+2.0+0.042 ms clock, 0.12+0.43/1.8/0.049+0.17 ms cpu, 4->4->1 MB, 5 MB goal, 4 P ...
gc # @#s #%: #+#+# ms clock, #+#/#/#+# ms cpu, #->#-># MB, # MB goal, # P
gc#
:GC 执行次数的编号,每次叠加。@#s
:自程序启动后到当前的具体秒数。#%
:自程序启动以来在GC中花费的时间百分比。#+...+#
:GC 的标记工做共使用的 CPU 时间占总 CPU 时间的百分比。#->#-># MB
:分别表示 GC 启动时, GC 结束时, GC 活动时的堆大小.#MB goal
:下一次触发 GC 的内存占用阈值。#P
:当前使用的处理器 P 的数量。gc 7 @0.140s 1%: 0.031+2.0+0.042 ms clock, 0.12+0.43/1.8/0.049+0.17 ms cpu, 4->4->1 MB, 5 MB goal, 4 P
0.031+2.0+0.042 ms clock:优化
0.12+0.43/1.8/0.049+0.17 ms cpu:spa
4->4->1 MB:
经过本章节咱们掌握了使用 GODEBUG 查看应用程序 GC 运行状况的方法,只要用这种方法咱们就能够观测不一样状况下 GC 的状况了,甚至能够作出很是直观的对比图,你们不妨尝试一下。