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

本文探讨了Go语言的垃圾回收(GC)机制,特别是三色标记法,分析了多标、漏标问题及其解决方案。文章强调了内存逃逸的影响,并列举了可能导致内存逃逸的典型情况。同时,提供了优化GC性能的实践建议,包括减少对象创建、避免内存逃逸等。此外,还介绍了用于性能调优的工具和参数设置。
摘要由CSDN通过智能技术生成

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。

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

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

  1. 标记出哪些对象是存活的,哪些是垃圾(可回收);
  2. 进行回收(清除/复制/整理),如果有移动过对象(复制/整理),还需要更新引用。

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

三色标记法的算法流程

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

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

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

  1. 初始时,所有对象都在 【白色集合】中;(搜索白色集合需要STW
  2. 将GC Roots 直接引用到的对象 挪到 【灰色集合】中;
  3. 从灰色集合中获取对象:
    3.1. 将本对象 引用到的 其他对象 全部挪到 【灰色集合】中;
    3.2. 将本对象 挪到 【黑色集合】里面。
  4. 重复步骤3,直至【灰色集合】为
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Aiky哇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值