golang 垃圾回收和内存逃逸分析
作者:互联网
1. golang垃圾回收
golang的垃圾回收算法是三色标记法,其中三个颜色分别为:灰色、黑色、白色,其对应了垃圾回收过程中变量的三种状态:
- 灰色:对象还在标记队列中等待
- 黑色:对象已经被标记,该对象不会在本次GC中被回收
- 白色:对象为被标记,该对象会在本地GC中被回收
1.1 垃圾回收流程
假设现在有这么几个对象:
有A-F六个对象,根对象a b为栈区分配的局部变量,根对象a b分别引用了对象 A B,而对象B有引用了对象D
下面简单看一下这几个对象的回收流程:
(1) 初始化:初始化时所有的对象都是白色
(2) 扫描:开始扫描根对象a b,由于a b引用了A B,所以A B设置为灰色
(3) 分析:分析对象A,A没有引用其他对象,将A设置为黑色,B引用了D对象,则将B设置为黑色的同时,将D设置为灰色
(4)结束:重复步骤3,直到灰色队列为空,这时黑色队列中的对象会被保留,白色队列会被回收
1.2 GC原理
GC的原理简单来讲就是标记内存中哪些还在使用,哪些不被使用,而不被使用的部分就是GC的对象。
root区域主要是指程序运行到当前时刻的栈和全局数据区域,是正在使用到的内存,当然应该优先标记,而考虑到内存块中可能存放的事指针,所以开需要扫描灰色队列进行递归标记,待灰色队列为空,就可以将白色标记回收
1.3 GC优化
golang的垃圾回收算法属于标记-清除,是需要STW的,在golang中就是要停掉所有goroutine,进行垃圾回收,待垃圾回收结束后再恢复goroutine。
所以,STW时间的长短直接影响了应用的执行,为了缩短STW时长,golang优化的GC算法,其中写屏障和辅助GC就是两种优化垃圾回收的方法
写屏障:
STW的目的是为了防止GC在扫描时出现内存变化而产生混乱,写屏障就是让goutine和GC同时运行,虽然不能完全消除STW,但可以大幅端减少STW时长。
写屏障在GC的特定时间开启,开启后指针传递时会把指针标记,即本轮不回收,下次GC时再确定。
辅助GC:
为了防止内存分配过快,在GC执行过程中,GC过程中mutator线程会并发运行,而mutator assisit机制会协助GC做一部分的工作。
1.4 GC触发机制
- 内存分配量达到阈值
- 定时触发
- 手动触发:runtime.GC()
1.5 GC调优
- 控制内存分配的速度,限制 Goroutine 的数量,从而提高赋值器对 CPU 的利用率
- 减少并复用内存,例如使用 sync.Pool 来复用需要频繁创建临时对象,例如提前分配足够的内存来降低多余的拷贝
- 需要时,增大 GOGC 的值,降低 GC 的运行频率
2. 内存逃逸分析
golang中堆栈对于程序员是透明的,栈空间回收更快,堆空间需要触发GC。
逃逸分析,可以尽量把那些不需要分配到堆上的变量直接分配到栈上。
2.1 逃逸分析
逃逸分析一个最基本的原则就是:如果一个函数返回对一个变量的引用,那么它就会发生逃逸
逃逸的常见情况:
- 发送指针的指针或值包含了指针到 channel 中,由于在编译阶段无法确定其作用域与传递的路径,所以一般都会逃逸到堆上分配
- slices 中的值是指针的指针或包含指针字段
- slice 由于 append 操作超出其容量,因此会导致 slice 重新分配
- 调用接口类型的方法
- 尽管能够符合分配到栈的场景,但是其大小不能够在在编译时候确定的情况,也会分配到堆上
2.2 如何避免内存逃逸
- 减少外部引用, 如指针
- 应该尽量避免使用接口类型
- 声明切片的时候,使用make指明初始大小,尽量避免append
标签:对象,回收,golang,逃逸,GC,内存,指针 来源: https://www.cnblogs.com/aganippe/p/16022689.html