每位 Gopher 都应该了解的 Golang 语言的垃圾回收算法

简介: 每位 Gopher 都应该了解的 Golang 语言的垃圾回收算法

01

介绍


关于垃圾回收,比较常见的算法有引用计数、标记清除和分代收集。Golang 语言使用的垃圾回收算法是标记清除。本文主要介绍一下 Golang 语言的垃圾回收算法。

02

Golang 语言 v1.3 及之前的垃圾回收 - 标记清除


Golang 语言的标记清除垃圾回收算法,为了防止 GC 扫描时内存变化引起的混乱,那么就需要 STW,即 Stop The World,具体在 Golang 语言中是指,在 GC 时,先停止所有 goroutine,再进行垃圾回收,等待垃圾回收结束后再恢复所有被停止的 goroutine。关于 STW 执行流程,可以参考下面这张经典图片。


640.jpg

「图片来自网络」

标记清除:

  1. 启动 STW,暂停程序的业务逻辑,找出不可达对象和可达对象。
  2. 将所有可达对象做标记。
  3. 清除未标记的对象。
  4. 停止 STW,程序继续执行。
  5. 循环往复,直到进程程序生命周期结束。

标记清除的缺点:

  1. STW 需要暂停程序,导致程序卡顿。
  2. 做标记需要扫描整个 heap(堆)。
  3. 清除数据会产生 head(堆)碎片。

标记清除的优化:

因为 STW 需要暂停程序,为了减少暂停程序的时间,将清除操作移出 STW 执行周期,但是优化效果不明显,进一步优化请继续阅读下文。

03

Golang 语言 v1.5 的垃圾回收 - 三色标记


所谓三色标记,实际上只是为了方便叙述而抽象出来的一种说法,三色对应垃圾回收过程中对象的三种状态:

  • 白色:对象未被标记,gcmarkBits 对应位为 0,该对象将会在本次 GC 中被清理。
  • 灰色:对象还在标记队列中等待被标记。
  • 黑色:对象已被标记,gcmarkBits 对应位为 0,该对象将会在本次 GC 中被回收。

三色标记:

  1. 新创建的对象,默认标记为白色。
  2. 从根节点开始遍历所有白色对象,将遍历到的对象的颜色由白色改为灰色。
  3. 将灰色对象作为根节点开始遍历所有白色对象,将遍历到的对象的颜色由白色改为灰色,并将作为根节点的灰色对象的颜色由灰色改为黑色。
  4. 循环往复,直到所有灰色对象的颜色都变为黑色。
  5. 将剩余的白色对象全部清除。

三色标记的缺点:

一个不被灰色对象可达的白色对象,如果被一个黑色对象引用,将会造成该白色对象丢失的问题。

三色标记的优化:

Golang 官方通过强/弱三色不变性,对三色标记做了优化。强三色不变性,即强制性不允许黑色对象引用白色对象;

弱三色不变性,即黑色对象可以引用白色对象,但是必须满足一个条件,该白色对象必须有灰色对象对它的直接引用,或者是可达链路中包含灰色对象。

具体实现是通过写屏障(Write Barrier),即在 GC 的特定时间开启,开启后指针传递时会把指针标记,被标记的指针在本次 GC 过程中不会被清理,等到下次 GC 时,才会被清理。写屏障的目的就是为了缩短 STW 的时间,让 goroutine 和 GC 同时运行。

Golang 语言中的写屏障分为插入写屏障和删除写屏障。

插入写屏障的含义:

满足强三色不变性,即被引用对象,会被强制标记为灰色。

插入写屏障的缺点:

结束时需要 STW 重新扫描栈,大约需要 10-100ms。

删除写屏障的含义:

满足弱三色不变性,即被删除对象,如果自身为灰色或者白色,会被标记为灰色。

删除写屏障的缺点:

回收精度低,即一个对象即使被删除了,最后一个指向该对象的指针也会等到下一次 GC 回收中才被清理。

04

Golang 语言 v1.8 的垃圾回收 - 混合写屏障


Golang 语言的团队为了更进一步优化垃圾回收,采用了混合写屏障。

混合写屏障:

  1. 后续无需 STW,GC 在首次执行时,先将栈上的所有对象都标记为黑色。
  2. GC 在执行过程中,在栈上新创建的对象,默认被标记为黑色。
  3. 将被删除的对象标记为灰色。
  4. 将被添加的对象标记为灰色。

混合写屏障的优点:

混合写屏障,满足弱三色不变性,结合了插入写屏障和删除写屏障的优点。

05

Golang 语言的 GC 触发方式

  1. 内存分配阈值,阈值=上次 GC 内存分配值 * 内存增长率,其中内存增长率由环境变量 GOGC 设定,默认值为 100。每次内存分配时,都会先检查当前内存分配是否已经达到阈值,如果已达到阈值,就会触发 GC,即每当内存分配量将要增长一倍时则触发 GC。
  2. 定时触发,src/runtime/proc.go 文件中的 forcegcperiod 设定触发 GC 的时间间隔,默认值为 2 分钟。
  3. 手动触发,通过调用 runtime.GC() 方法,触发 GC。

06

调式 GC


Golang 语言使用 GODEBUG 调式 GC: GODEBUG=gctrace=1 go run main.go

输出结果:

gc 1 @0.013s 0%: 0.037+0.36+0.004 ms clock, 0.60+0.48/0.81/0.012+0.073 ms cpu, 4->4->0 MB, 5 MB goal, 16 P
gc 2 @0.016s 1%: 0.010+0.24+0.004 ms clock, 0.17+0.29/0.44/0.21+0.064 ms cpu, 4->4->0 MB, 5 MB goal, 16 P
gc 3 @0.019s 1%: 0.069+0.50+0.041 ms clock, 1.1+0.29/0.68/0.13+0.66 ms cpu, 4->4->0 MB, 5 MB goal, 16 P
gc 4 @0.021s 2%: 0.056+0.35+0.041 ms clock, 0.90+0.33/0.67/0.064+0.65 ms cpu, 4->4->0 MB, 5 MB goal, 16 P
gc 5 @0.023s 2%: 0.053+0.27+0.003 ms clock, 0.85+0.42/0.63/0.069+0.057 ms cpu, 4->4->0 MB, 5 MB goal, 16 P

输出结果的含义:

  1. gc 1 @0.013s 0%: 表示第 1 次执行 GC,0.013s 表示执行时间。
  2. 0% GC 占用进程的进程 CPU 时间的百分比。
  3. 0.037+0.36+0.004 ms clock 表示 GC 耗时,依次是 STW 清扫的时间,并发标记和扫描的时间,STW 标记的时间,即 stop-the-world (STW) sweep termination + concurrent mark and scan + and STW mark termination
  4. 0.60+0.48/0.81/0.012+0.073 ms cpu GC 占用的 CPU 时间。
  5. 4->4->0 MB 依次表示堆的大小,GC 后堆的大小,存活堆的大小。
  6. 5 MB goal 表示整体堆的大小。
  7. 16 P 表示 CPU 的核心数。
  8. GC forced 表示调用 runtime.GC() 方法,手动执行 GC。

scvg0: inuse: 6, idle: 12, sys: 18, released: 0, consumed: 18 (MB)  
scvg0: inuse: 6, idle: 9, sys: 15, released: 0, consumed: 15 (MB)  
GC forced  

  • inuse:内存使用大小。
  • idle:需要清除的空闲内存。
  • sys: 系统映射的内存。
  • released:释放的系统内存。
  • consumed:申请的系统内存。

07

总结

本文通过 Golang 语言的 v1.3、v1.5 和 v1.8 三个版本的 Golang 语言的算法的演进介绍垃圾回收。实际上几乎每个版本都会涉及垃圾回收的优化,相关代码也越来越复杂。如果读者希望更深入了解垃圾回收相关的内容,建议阅读相关源码。

尽管 Golang 语言可以自动进行垃圾回收,但是 GC 也会消耗资源,尽量还是在编写 Golang 代码的时候减少对象分配的数量,采用对象复用、将小对象组合成大对象或采用精准的数据类型,比如可以使用 int8,绝不使用 int。还可以在编写 Golang 代码的时候,手动触发 GC,将不再使用的内存及时释放。




目录
打赏
0
0
0
0
8
分享
相关文章
员工上网行为监控中的Go语言算法:布隆过滤器的应用
在信息化高速发展的时代,企业上网行为监管至关重要。布隆过滤器作为一种高效、节省空间的概率性数据结构,适用于大规模URL查询与匹配,是实现精准上网行为管理的理想选择。本文探讨了布隆过滤器的原理及其优缺点,并展示了如何使用Go语言实现该算法,以提升企业网络管理效率和安全性。尽管存在误报等局限性,但合理配置下,布隆过滤器为企业提供了经济有效的解决方案。
98 8
员工上网行为监控中的Go语言算法:布隆过滤器的应用
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
这篇文章详细介绍了Dijkstra和Floyd算法,这两种算法分别用于解决单源和多源最短路径问题,并且提供了Java语言的实现代码。
127 3
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
|
3天前
|
公司局域网管理系统里的 Go 语言 Bloom Filter 算法,太值得深挖了
本文探讨了如何利用 Go 语言中的 Bloom Filter 算法提升公司局域网管理系统的性能。Bloom Filter 是一种高效的空间节省型数据结构,适用于快速判断元素是否存在于集合中。文中通过具体代码示例展示了如何在 Go 中实现 Bloom Filter,并应用于局域网的 IP 访问控制,显著提高系统响应速度和安全性。随着网络规模扩大和技术进步,持续优化算法和结合其他安全技术将是企业维持网络竞争力的关键。
17 1
公司局域网管理系统里的 Go 语言 Bloom Filter 算法,太值得深挖了
内网监控管理软件:PHP 语言队列算法揭秘
在数字化办公环境中,内网监控管理软件对企业的稳定运行和信息安全至关重要。本文深入介绍PHP中的队列算法及其在内网监控软件中的应用,包括监控数据收集、任务调度和日志记录等场景,通过代码示例展示其实现方法。队列算法可提高性能、保证数据顺序并实现异步处理,为企业提供高效的安全保障。
15 1
探秘员工泄密行为防线:基于Go语言的布隆过滤器算法解析
在信息爆炸时代,员工泄密行为对企业构成重大威胁。本文聚焦布隆过滤器(Bloom Filter)这一高效数据结构,结合Go语言实现算法,帮助企业识别和预防泄密风险。通过构建正常操作“指纹库”,实时监测员工操作,快速筛查可疑行为。示例代码展示了如何利用布隆过滤器检测异常操作,并提出优化建议,如调整参数、结合日志分析系统等,全方位筑牢企业信息安全防线,守护核心竞争力。
|
30天前
|
内网监控系统之 Go 语言布隆过滤器算法深度剖析
在数字化时代,内网监控系统对企业和组织的信息安全至关重要。布隆过滤器(Bloom Filter)作为一种高效的数据结构,能够快速判断元素是否存在于集合中,适用于内网监控中的恶意IP和违规域名筛选。本文介绍其原理、优势及Go语言实现,提升系统性能与响应速度,保障信息安全。
30 5
探秘局域网桌面监控:深入剖析 Java 语言核心算法
在数字化办公时代,局域网桌面监控如同企业的“智慧鹰眼”,确保工作效率与数据安全。本文以Java为载体,揭示哈希表在监控中的关键应用。通过高效的数据结构和算法,哈希表能快速索引设备连接信息,大幅提升监控的时效性和响应速度。代码示例展示了如何用Java实现设备网络连接监控,结合未来技术如AI、大数据,展望更智能的监控体系,助力企业在数字化浪潮中稳健前行。
|
2月前
|
Go 语言中实现 RSA 加解密、签名验证算法
随着互联网的发展,安全需求日益增长。非对称加密算法RSA成为密码学中的重要代表。本文介绍如何使用Go语言和[forgoer/openssl](https://github.com/forgoer/openssl)库简化RSA加解密操作,包括秘钥生成、加解密及签名验证。该库还支持AES、DES等常用算法,安装简便,代码示例清晰易懂。
65 12
解锁企业计算机监控的关键:基于 Go 语言的精准洞察算法
企业计算机监控在数字化浪潮下至关重要,旨在保障信息资产安全与高效运营。利用Go语言的并发编程和系统交互能力,通过进程监控、网络行为分析及应用程序使用记录等手段,实时掌握计算机运行状态。具体实现包括获取进程信息、解析网络数据包、记录应用使用时长等,确保企业信息安全合规,提升工作效率。本文转载自:[VIPShare](https://www.vipshare.com)。
40 1
|
3月前
|
JVM有哪些垃圾回收算法?
(1)标记清除算法: 标记不需要回收的对象,然后清除没有标记的对象,会造成许多内存碎片。 (2)复制算法: 将内存分为两块,只使用一块,进行垃圾回收时,先将存活的对象复制到另一块区域,然后清空之前的区域。用在新生代 (3)标记整理算法: 与标记清除算法类似,但是在标记之后,将存活对象向一端移动,然后清除边界外的垃圾对象。用在老年代
34 0

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等