Golang之垃圾回收
本篇主要是参考了:
http://legendtkl.com/2017/04/28/golang-gc/
说是参考,但其实基本上是原封不动。
GC算法简介:
1. 引用计数
引用计数的思想非常简单:每个单元维护一个域,保存其它单元指向它的引用数量(类似有向图的入度)。当引用数量为 0 时,将其回收。引用计数是渐进式的,能够将内存管理的开销分布到整个程序之中。C++ 的 share_ptr 使用的就是引用计算方法。
引用计数算法实现一般是把所有的单元放在一个单元池里,比如类似 free list。这样所有的单元就被串起来了,就可以进行引用计数了。新分配的单元计数值被设置为 1(注意不是 0,因为申请一般都说 ptr = new object 这种)。每次有一个指针被设为指向该单元时,该单元的计数值加 1;而每次删除某个指向它的指针时,它的计数值减 1。当其引用计数为 0 的时候,该单元会被进行回收。虽然这里说的比较简单,实现的时候还是有很多细节需要考虑,比如删除某个单元的时候,那么它指向的所有单元都需要对引用计数减 1。那么如果这个时候,发现其中某个指向的单元的引用计数又为 0,那么是递归的进行还是采用其他的策略呢?递归处理的话会导致系统颠簸。关于这些细节这里就不讨论了,可以参考文章后面的给的参考资料。
优点
- 渐进式。内存管理与用户程序的执行交织在一起,将 GC 的代价分散到整个程序。不像标记-清扫算法需要 STW (Stop The World,GC 的时候挂起用户程序)。
- 算法易于实现。
- 内存单元能够很快被回收。相比于其他垃圾回收算法,堆被耗尽或者达到某个阈值才会进行垃圾回收。
缺点
- 原始的引用计数不能处理循环引用。大概这是被诟病最多的缺点了。不过针对这个问题,也除了很多解决方案,比如强引用等。
- 维护引用计数降低运行效率。内存单元的更新删除等都需要维护相关的内存单元的引用计数,相比于一些追踪式的垃圾回收算法并不需要这些代价。
- 单元池 free list 实现的话不是 cache-friendly 的,这样会导致频繁的 cache miss,降低程序运行效率。
2. 标记清扫
标记-清扫算法是第一种自动内存管理,基于追踪的垃圾收集算法。算法思想在 70 年代就提出了,是一种非常古老的算法。内存单元并不会在变成垃圾立刻回收,而是保持不可达状态,直到到达某个阈值或者固定时间长度。这个时候系统会挂起用户程序,也就是 STW,转而执行垃圾回收程序。垃圾回收程序对所有的存活单元进行一次全局遍历确定哪些单元可以回收。算法分两个部分:标记(mark)和清扫(sweep)。标记阶段表明所有的存活单元,清扫阶段将垃圾单元回收。标记-清扫算法的优点也就是基于追踪的垃圾回收算法具有的优点:避免了引用计数算法的缺点(不能处理循环引用,需要维护指针)。缺点也很明显,需要 STW。
改进:三色标记算法:
三色标记算法是对标记阶段的改进,原理如下:
- 起初所有对象都是白色。
- 从根出发扫描所有可达对象,标记为灰色,放入待处理队列。
- 从队列取出灰色对象,将其引用对象标记为灰色放入队列,自身标记为黑色。
- 重复 3,直到灰色对象队列为空。此时白色对象即为垃圾,进行回收。
三色标记的一个明显好处是能够让用户程序和 mark 并发的进行,具体可以参考论文:《On-the-fly garbage collection: an exercise in cooperation.》。Golang 的 GC 实现也是基于这篇论文
3. 节点复制
节点复制也是基于追踪的算法。其将整个堆等分为两个半区(semi-space),一个包含现有数据,另一个包含已被废弃的数据。节点复制式垃圾收集从切换(flip)两个半区的角色开始,然后收集器在老的半区,也就是 Fromspace 中遍历存活的数据结构,在第一次访问某个单元时把它复制到新半区,也就是 Tospace 中去。在 Fromspace 中所有存活单元都被访问过之后,收集器在 Tospace 中建立一个存活数据结构的副本,用户程序可以重新开始运行了。
优点
- 所有存活的数据结构都缩并地排列在 Tospace 的底部,这样就不会存在内存碎片的问题。
- 获取新内存可以简单地通过递增自由空间指针来实现。
缺点
- 内存得不到充分利用,总有一半的内存空间处于浪费状态。
4. 分代收集
基于追踪的垃圾回收算法(标记-清扫、节点复制)一个主要问题是在生命周期较长的对象上浪费时间(长生命周期的对象是不需要频繁扫描的)。同时,内存分配存在这么一个事实 “most object die young”。基于这两点,分代垃圾回收算法将对象按生命周期长短存放到堆上的两个(或者更多)区域,这些区域就是分代(generation)。对于新生代的区域的垃圾回收频率要明显高于老年代区域。
分配对象的时候从新生代里面分配,如果后面发现对象的生命周期较长,则将其移到老年代,这个过程叫做 promote。随着不断 promote,最后新生代的大小在整个堆的占用比例不会特别大。收集的时候集中主要精力在新生代就会相对来说效率更高,STW 时间也会更短。
优点
- 性能更优。
缺点
- 实现复杂
Golang的垃圾回收
Golang的垃圾回收主要是基于三色标记法。在三色标记法的基础上,增加了写屏障(write barrier)。

写屏障,单纯理解,就是在新建或修改引用时,不允许从黑色标记指向白色标记。因为黑色标记是已经完成遍历的节点。增加从黑到白的指向,会导致在垃圾回收的时候对该白色标记节点进行回收。
具体的情况:Golang 1.7 之前的 write barrier 使用的经典的 Dijkstra-style insertion write barrier [Dijkstra ‘78], STW 的主要耗时就在 stack re-scan 的过程。自 1.8 之后采用一种混合的 write barrier 方式 (Yuasa-style deletion write barrier [Yuasa ‘90] 和 Dijkstra-style insertion write barrier [Dijkstra ‘78])来避免 re-scan。
Golang之垃圾回收的更多相关文章
- golang的垃圾回收(GC)机制
golang的垃圾回收采用的是 标记-清理(Mark-and-Sweep) 算法 就是先标记出需要回收的内存对象快,然后在清理掉: 在这里不介绍标记和清理的具体策略(可以参考https://lengz ...
- 从golang的垃圾回收说起(上篇)
本文来自网易云社区 1 垃圾回收中的重要概念 1.1 定义 In computer science, garbage collection (GC) is a form of automatic me ...
- Golang GC 垃圾回收机制详解
摘要 在实际使用 go 语言的过程中,碰到了一些看似奇怪的内存占用现象,于是决定对go语言的垃圾回收模型进行一些研究.本文对研究的结果进行一下总结. 什么是垃圾回收? 曾几何时,内存管理是程序员开发应 ...
- 从golang的垃圾回收说起(下篇)
文章来自网易云社区 4 Golang垃圾回收的相关参数 4.1 触发GC gc触发的时机:2分钟或者内存占用达到一个阈值(当前堆内存占用是上次gc后对内存占用的两倍,当GOGC=100时) # 表示 ...
- golang GC 垃圾回收机制
垃圾回收(Garbage Collection,简称GC)是编程语言中提供的自动的内存管理机制,自动释放不需要的对象,让出存储器资源,无需程序员手动执行. Golang中的垃圾回收主要应用三色标记法, ...
- 【GoLang】golang垃圾回收 & 性能调优
golang垃圾回收 & 性能调优 参考资料: 如何监控 golang 程序的垃圾回收_Go语言_第七城市 golang的垃圾回收(GC)机制 - 两只羊的博客 - 博客频道 - CSDN.N ...
- Golang——垃圾回收GC(2)
1 垃圾回收中的重要概念 1.1 定义 In computer science, garbage collection (GC) is a form of automatic memory manag ...
- golang 垃圾回收机制
用任何带 GC 的语言最后都要直面 GC 问题.在以前学习 C# 的时候就被迫读了一大堆 .NET Garbage Collection 的文档.最近也学习了一番 golang 的垃圾回收机制,在这里 ...
- golang 垃圾回收 gc
http://ruizeng.net/golang-gc-internals/ 摘要 在实际使用go语言的过程中,碰到了一些看似奇怪的内存占用现象,于是决定对go语言的垃圾回收模型进行一些研究.本文对 ...
随机推荐
- LAMP搭建示例
lamp介绍 其实就是由Linux+Apache+Mysql/MariaDB+Php/Perl/Python的一组动态网站或者服务器的开源软件,除Linux外其它各部件本身都是各自独立的程序,但是因为 ...
- Vs2017编译器提示:不能将“const char *”类型的值分配到“char *”类型的实体
在项目属性中将语言符合模式改成否即可
- 第三章 IP地址规划设计技术(很重要)
知识重点: 选择题考点 IP基础(网络地址.子网掩码) 网络地址转换 NAT 的原理 CIDR (计算方法) IPv6 地址表示 综合题 IP地址的分类与计算 VLSM 地址规划 3.1 基础知识 3 ...
- Java 实现简单的 Socket 通信
Java socket 封装了传输层的实现细节,开发人员可以基于 socket 实现应用层.本文介绍了 Java socket 简单用法. 1. 传输层协议 传输层包含了两种协议,分别是 TCP (T ...
- 攻防世界_MISC进阶区_Get-the-key.txt(详细)
攻防世界MISC进阶之Get-the-key.txt 啥话也不说,咱们直接看题吧! 首先下载附件看到一个压缩包: 我们直接解压,看到一个文件,也没有后缀名,先用 file 看一下文件属性: 发现是是L ...
- Win 10 Docker安装和简单使用
Win 10 Docker安装和简单使用 1.环境准备 Docker for Windows需要运行在64位Windows 10 Pro专业版.企业版或教育版(1607年纪念更新,版本14393或更高 ...
- 有了链路日志增强,排查Bug小意思啦!
在工作中,相信大家最怕的一件事就是听到有人在工作群艾特你:某某功能报错啦... 然后你就得屁颠屁颠的去服务器看日志,日志量少还好点,多的话找起来太麻烦了.不太容易直接定位到关键地方. 东找找西找找,好 ...
- 在IDEA中通过Module管理多个项目
你身边有没有这种顽固的Eclipse忠实用户:IDEA不能一个窗口管理多个项目!太不方便了! 对于一个窗口同时管理多个项目的需求,在我们日常开发时候是经常需要的.尤其当我们在分布式环境下,在一个窗口中 ...
- 【Linux】fdisk -l发现有加号"+"
今天分区不够了,打算扩下分区,想起当时创建机器的时候还有大约80多个G没有用,今天打算重新利用上 就用了fdisk /dev/sda 创建了两个分区,但是发现分区下面有加号,感到而别的困惑 最后在很多 ...
- printf函数输出格式总结
printf函数格式 函数描述: printf("[格式化字符串]", [参数链表]); 函数声明: int printf(const char *format, ...) ; 输 ...