GC
如何观察go语言垃圾回收现象
- GODEBUG=gctrace=1
- go tool trace 命令
- debug.ReadGCStats
- runtime.ReadMemStats
有了垃圾回收为什么还会内存泄漏
- 预期能被快速释放的内存因被根对象引用而没有得到迅速释放,如一个大全局对象
- goroutine 泄漏
GC ROOT
为什么不选择标记整理
为什么不选择分代
- 分代回收的目的是主要回收新创建的对象,GO语言会通过逃逸分析将大部分对象分配到栈上,堆上大多数为老对象,分代回收意义不大
并发GC的问题
- 漏标:扫描完的黑色对象引入了白色引用
- 错标:扫描中的灰色对象,引用被删除
三色标记
- 白色、灰色和黑色。白色对象表示该对象尚未被标记。灰色对象表示该对象正在被标记。黑色对象表示该对象已经被标记,并且其所有子对象也已经被标记
破坏垃圾回收正确性的条件
- 赋值器修改对象图,导致某一黑色对象引用白色对象
- 从灰色对象出发,到达白色对象的、未经访问过的路径被赋值器破坏
- 强三色不变式:白色对象不能被黑色对象直接引用 ,
- 弱三色不变式:白色对象可以被黑色对象引用,但要从某个灰对象出发仍然可达该白对象
- 插入写屏障:保证当一个黑色对象指向一个白色对象前,会先触发屏障将白色对象置为灰色,满足强三色不变
- 删除写屏障:保证当一个白色对象即将被上游删除引用前,会触发屏障将其置灰,之后再删除上游指向其的引用
存在的问题
- 没有解决错标的问题,
- 指针赋值都需要插入写屏障,增大性能开销,所以在栈上没有启动写屏障
混合写屏障:
- GC 开始前,以栈为单位分批扫描,将栈中所有对象置黑
- GC 期间,栈上新创建对象直接置黑
- 堆对象正常启用插入写屏障
- 堆对象正常启用删除写屏障
历程
- Go 1.3 之前: 使用标记-清除算法。
- Go 1.5: 引入了三色标记算法。
- Go 1.8: 引入了混合写屏障。
GC触发时机
- 主动触发,通过调用 runtime.GC 来触发 GC,此调用阻塞式地等待当前 GC 运行完毕。
- 被动触发:
- 使用系统监控,当超过两分钟没有产生任何 GC 时,强制触发 GC
- 对象分配时触发,使用步调(Pacing)算法,其核心思想是控制内存增长的比例
如果内存分配速度超过了标记清除的速度怎么办
- 在 mallocgc 调用时进行检查。当存在新的内存分配时,会暂停分配内存过快的那些
goroutine,并将其转去执行一些辅助标记(Mark Assist)的工作,从而达到放缓继续分配、辅助
GC 的标记工作的目的
GC调优
- 99%的应用程序都不需要针对 GC 进行调优。
- 合理化内存分配的速度、提高赋值器的 CPU 利用率
- 降低并复用已经申请的内存
- 可以非常简单粗暴地将 GOGC 调整为 1000
两次STW
- 清扫终止和标记终止都需要STW
- 并发标记前需要开启写屏障,标记后需要关闭写屏障,再清扫
GC原理
- 并发标记主要是标记msapn中的gcmarks字段,通过这个字段可以查看内存块哪个是存货的
文档
文档