原文地址:使用GODEBUG看GC什么是GC在计算机科学中,垃圾回收(GC)是一种自动管理内存的机制。垃圾收集器会尝试回收对象及其占用的内存。而最早的JohnMcCarthy在1959年左右发明了垃圾收集,以简化Lisp中手动内存管理的机制(来自wikipedia)。为什么需要GC手动管理内存?很麻烦。管理不善或者内存泄漏也很糟糕,会直接导致程序不稳定(不断泄漏)甚至直接崩溃。GC带来的问题如果要说它会带来什么问题,那就是大家最关心的StopTheWorld(STW)了。STW是指当执行一个垃圾回收算法的某个阶段时,需要暂停整个应用程序来处理GC相关的工作项。例如:行为会是STW吗?为什么标记开始?标记开始时,根对象的扫描会准备好,写屏障(WriteBarrier)和辅助GC(mutatorassist)会开启,收集器和应用程序并发运行,所以会suspend所有当前正在运行的Goroutines。并发标记中不标记阶段,主要目的是标记堆内存中仍在使用的值。标记结束时,标记任务完成后,会重新扫描一些根对象。此时写屏障(WriteBarrier)和辅助GC(mutatorassist)会被禁用,标记阶段和应用程序并发运行,所以在标记阶段有可能会有新的对象产生,所以STW重新扫描时需要。如何调整GC频率您可以通过GOGC变量设置初始垃圾收集器的目标百分比值。比较规则是当新分配的值与上次回收后剩余的实时值的比例达到设定的目标百分比时,就会触发GC。默认值为GOGC=100。如何将其设置为GOGC=off以完全禁用垃圾收集器?简单来说,GOGC的值设置越大,GC的频率越低,但是每次GC触发的堆内存会越大。各版本版本GC算法的GC状态STW时间Go1.0STW(强依赖tcmalloc)100ms到秒级Go1.3MarkSTW,Sweepparallel100mslevelGo1.5三色标记方式,并发标记清除。同时从C和少量汇编改成Go和少量汇编,达到10-50ms的水平。Go1.61.5一些与并发GC不协调的变化,集中GC协调协程,改为状态机,达到5ms级别Go1.7GC中,标记栈收缩改为并发,span对象分配机制由freelist改为bitmap模式,并且SSA引入了ms-levelGo1.8hybridwritebarrier(混合写入屏障),消除了重新扫描stacksubmsGo1.12MarkTermination过程优化subms,但几乎减少了一半。注:信息来源于@boya在深圳GopherMeetup的分享。GODEBUGGODEBUG变量可以控制运行时的调试变量,参数之间用逗号分隔,格式为:name=val。本文重点观察GC,主要涉及gctrace参数。设置gctrace=1后,我们可以让垃圾回收器将GC运行信息发送到标准错误流中。涉及术语标记:标记阶段。markTermination:标记结束阶段。mutatorassist:AssistedGC是指mutator线程在GC过程中会并发运行,mutatorassist机制会辅助GC做部分工作。heap_live:在Go的内存管理中,span是内存页的基本单位,每页大小为8kb,Go会根据对象的大小分配不同页的span,heap_live表示所有span的总大小。dedicated/fractional/idle:在标记阶段,有dedicated、fractional和idle三种不同的markworker模式,分别代表不同的专注程度。其中dedicated模式是最专用的,是一个完整的GC回收行为,fractional模式只会做一部分的GC行为,idle是最简单的。在这里你只需要明白它是不同专注程度的markworker就可以了。详细的介绍,我们可以等待后续文章。表演代码funcmain(){wg:=sync.WaitGroup{}wg.Add(10)fori:=0;我<10;i++{gofunc(wg*sync.WaitGroup){varcounterintfori:=0;我<1e10;i++{counter++}wg.Done()}(&wg)}wg.Wait()}gctrace$GODEBUG=gctrace=1gorunmain.gogc1@0.032s0%:0.019+0.45+0.003毫秒时钟,0.076+0.22/0.40/0.80+0.012mscpu,4->4->0MB,5MBgoal,4Pgc2@0.046s0%:0.004+0.40+0.008msclock,0.017+0.32/0.25/0.81+0.034mscpu,4->4->0MB,5MB目标,4Pgc3@0.063s0%:0.004+0.40+0.008ms时钟,0.018+0.056/0.32/0.64+0.033mscpu,4->4->0MB,5MB目标,4Pgc4@0.080s0%:0.004+0.45+0.016ms时钟,0.018+0.15/0.34/0.77+0.065mscpu,4->4->1MB,5MB目标,4Pgc5@0.095s0%:0.015+0.87+0.005ms时钟,0.061+0.27/0.74/1.8+0.023mscpu,4->4->1MB,5MB目标,4Pgc6@0.113s0%:0.014+0.69+0.002毫秒时钟,0.056+0.23/0.48/1.4+0.011mscpu,4->4->1MB,5MB目标,4Pgc7@0.140s1%:0.031+2.0+0.042ms时钟,0.12+0.43/1.8/0.049+0.17mscpu,4->4->1MB,5MBgoal,4P...formatgc#@#s#%:#+#+#msclock,#+#/#/#+#mscpu,#->#->#MB,#MB目标,#P含义gc#:GC执行次数,每次叠加@#s:从程序开始到当前时间的具体秒数。#%:自程序启动以来花费在GC上的时间百分比。#+...+#:GC标记任务使用的总CPU时间的百分比。#->#->#MB:代表GC开始时、GC结束时、GC活跃时的堆大小。#MBgoal:下次触发GC的内存使用阈值。#P:当前使用的处理器数量P。Casegc7@0.140s1%:0.031+2.0+0.042msclock,0.12+0.43/1.8/0.049+0.17mscpu,4->4->1MB,5MBgoal,4Pgc7:7thGC。@0.140s:目前是程序启动后0.140s。1%:程序启动后有1%的时间花在了GC上。0.031+2.0+0.042msclock:0.031:表示标记阶段单个P的STW时间。2.0:表示所有P的标记并发(concurrentmarking)所用的时间。0.042:表示单个P的markTermination阶段的STW时间。0.12+0.43/1.8/0.049+0.17mscpu:0.12:表示整个进程在mark阶段的STW停顿时间。0.43/1.8/0.049:0.43表示mutatorassist耗时,1.8表示dedicated+fractional耗时,0.049表示idle耗时。0.17ms:0.17表示整个过程在markTermination阶段的STW时间。4->4->1MB:4:表示开始mark阶段前的heap_live大小。4:表示在markTermination阶段开始前的heap_live大小。1:表示标记对象的大小。5MBgoal:表示下一个触发GC回收的阈值是5MB。4P:本次GC涉及了多少个P。小结通过本章,我们掌握了使用GODEBUG查看应用GC运行情况的方法。我们只要使用这种方法,就可以观察到不同情况下的GC情况,甚至可以做出非常直观的对比图。你不妨试一试。相关文章使用GODEBUG查看调度和跟踪参考GoGC打印出这些信息的意义?GODEBUG之gctrace分析对GolangGC的一些误解——它真的领先于JavaGC吗?@boyaGolangRuntimePPT简单来说
