go的gc机制,内存泄漏和内存逃逸,怎样才能最大减少gc的负担?

go的gc机制,内存泄漏和内存逃逸,怎样才能最大减少gc的负担?

gc也就是垃圾回收。最近写的项目,pprof查看性能,发现在gc的消耗非常大,发现gc的cpu占用已经到了30%。在oom的时候更是要2分钟才开始进行gc。

因此想着深入调研一下go的gc模式,了解在写程序时保持哪些好的代码习惯,才能最大的减少程序的gc调用。

目录

gc是什么

gc的主流算法

三色标记法

三色标记法的算法流程

三色标记法的问题

多标的例子

多标的发生条件

漏标的例子

漏标的发生条件

解决漏标问题的方法

三色标记法的缺陷

go的内存逃逸

能引起变量逃逸到堆上的典型情况

go的习惯,如何尽可能避免gc的性能影响

go的gc性能调优工具

参考资料:

gc是什么

了解的同学可以直接跳过。

GC Garbage Collection。直译过来就是垃圾回收。想要进行程序调优,是肯定避不开这个环节的,gc管理不好,很容易造成程序的内存无限增长,然后被系统杀掉,上线的项目发生内存泄漏,肯定是p0级别的问题了。

一般写的程序中会用到两种内存,堆内存和栈内存。堆内存就是堆状数据结构存储的内存,不连续,动态分配,存取慢,系统不会自动帮你释放;栈内存就是连续的内存存储结构,先进后出,存取速度快,仅次于寄存器,但是数据大小和生命周期确定,系统会自动释放的。

像函数中定义的一些局部变量,外部没有办法访问,用完就释放掉,这些栈内存中的数据管理起来相对简单,不需要人工的干预释放,所以GC是不会管栈中的内存的,GC负责清理的只有在堆中的内存。

在C等比较早期的语言中是没有堆内存管理的,在堆中申请和释放内存都需要手动执行,这样很容易出现忘记释放内存的情况,导致内存泄漏。所以诞生了一个更人性化的工具,就是GC,它可以自动管理内存的申请和释放,避免造成内存泄漏。当然,有得必有失,有了GC工具,也就需要额外性能或者内存的开支。

gc的主流算法

gc有两种主流的算法,一个是 【引用计数】,一个是【可达性分析】。只是大类,具体实现的算法就有很多了。

【引用计数】顾名思义,就是对每个使用的变量进行计数统计,每被引用一次,那么计数+1,否则计数-1,到0就可以回收了。

【可达性分析】通过引用的链路来判断是否可以回收,能访问到的就是正在使用的,不能回首;所有不能访问到的,是可以回收的。

当前可达性分析要更主流一些。Go、Java、.Net等都是如此。因为【引用计数】虽然更简单,实时性更好,但是有个很严重的问题,是无法处理循环引用的,比如A->B,B->C,C->A。这种所有的引用均为1,除非主动断开其中一条链,否则不会触发回收。所以【引用计数】的方式一般会和【可达性分析】一起使用。

三色标记法

go的内存回收算法,三色标记法,也是属于可达性分析算法的一种。

先了解一个算法Mark-And-Sweep(标记清扫),这个算法就是严格按照追踪式算法的思路来实现的。这个算法会设置一个标志位来记录对象是否被使用。最开始所有的标记位都是 0,如果发现对象是可达的就会置为 1,一步步下去就会呈现一个类似树状的结果。等标记的步骤完成后,会将未被标记的对象统一清理,再次把所有的标记位设置成 0 方便下次清理。

这个算法最大的问题是 GC 执行期间需要把整个程序完全暂停,不能异步进行 GC 操作。因为在不同阶段标记清扫法的标志位 0 和 1 有不同的含义,那么新增的对象无论标记为什么都有可能意外删除这个对象。

对实时性要求高的系统来说,Mark-And-Sweep这种需要长时间挂起的标记清扫法是不可接受的。所以就需要一个算法来解决 GC 运行时程序长时间挂起的问题,三色标记法就是干这个的。

三色标记最大的好处是可以异步执行,从而可以以中断时间极少的代价或者完全没有中断来进行整个 GC。

注意,三色标记法虽然是异步的,但还是会有中断的时间。

垃圾回收器的工作流程大体如下:

标记出哪些对象是存活的,哪些是垃圾(可回收);

进行回收(清除/复制/整理),如果有移动过对象(复制/整理),还需要更新引用。

无论使用哪种算法,标记总是必要的一步。而三色标记法的中断时间就在于刚开始标记的时候。所以需要知道一个概念,叫「Stop The World 」,简称「STW」。

三色标记法的算法流程

把遍历对象图过程中遇到的对象,按“是否访问过”这个条件标记成以下三种颜色:

白色:尚未访问过。

黑色:本对象已访问过,而且本对象 引用到 的其他对象 也全部访问过了。

灰色:本对象已访问过,但是本对象 引用到 的其他对象 尚未全部访问完。全部访问后,会转换为黑色。

假设现在有白、灰、黑三个集合(表示当前对象的颜色),其遍历访问过程为:

初始时,所有对象都在 【白色集合】中;(搜索白色集合需要STW)

将GC Roots 直接引用到的对象 挪到 【灰色集合】中;

从灰色集合中获取对象: 3.1. 将本对象 引用到的 其他对象 全部挪到 【灰色集合】中; 3.2. 将本对象 挪到 【黑色集合】里面。

重复步骤3,直至【灰色集合】为空时结束。

结束后,仍在【白色集合】的对象即为GC Roots 不可达,可以进行回收。

相关数据

滞欷的意思
365是英国的哪家公司

滞欷的意思

⌛ 06-29 👁️ 3828