golang 垃圾回收机制
用任何带 GC 的语言最后都要直面 GC 问题。在以前学习 C# 的时候就被迫读了一大堆 .NET Garbage Collection 的文档。最近也学习了一番 golang 的垃圾回收机制,在这里记录一下。
常见 GC 算法
趁着这个机会我总结了一下常见的 GC 算法。分别是:引用计数法、Mark-Sweep法、三色标记法、分代收集法。
1. 引用计数法
原理是在每个对象内部维护一个整数值,叫做这个对象的引用计数,当对象被引用时引用计数加一,当对象不被引用时引用计数减一。当引用计数为 0 时,自动销毁对象。
目前引用计数法主要用在 c++ 标准库的 std::shared_ptr 、微软的 COM 、Objective-C 和 PHP 中。
但是引用计数法有个缺陷就是不能解决循环引用的问题。循环引用是指对象 A 和对象 B 互相持有对方的引用。这样两个对象的引用计数都不是 0 ,因此永远不能被收集。
另外的缺陷是,每次对象的赋值都要将引用计数加一,增加了消耗。
2. Mark-Sweep法(标记清除法)
这个算法分为两步,标记和清除。
- 标记:从程序的根节点开始, 递归地 遍历所有对象,将能遍历到的对象打上标记。
- 清除:讲所有未标记的的对象当作垃圾销毁。
图片来自 https://en.wikipedia.org/wiki/Tracing_garbage_collection
如图所示。
但是这个算法也有一个缺陷,就是人们常常说的 STW 问题(Stop The World)。因为算法在标记时必须暂停整个程序,否则其他线程的代码可能会改变对象状态,从而可能把不应该回收的对象当做垃圾收集掉。
当程序中的对象逐渐增多时,递归遍历整个对象树会消耗很多的时间,在大型程序中这个时间可能会是毫秒级别的。让所有的用户等待几百毫秒的 GC 时间这是不能容忍的。
golang 1.5以前使用的这个算法。
3. 三色标记法
三色标记法是传统 Mark-Sweep 的一个改进,它是一个并发的 GC 算法。
原理如下,
- 首先创建三个集合:白、灰、黑。
- 将所有对象放入白色集合中。
- 然后从根节点开始遍历所有对象(注意这里并不递归遍历),把遍历到的对象从白色集合放入灰色集合。
- 之后遍历灰色集合,将灰色对象引用的对象从白色集合放入灰色集合,之后将此灰色对象放入黑色集合
- 重复 4 直到灰色中无任何对象
- 通过write-barrier检测对象有变化,重复以上操作
- 收集所有白色对象(垃圾)
图片来自 https://en.wikipedia.org/wiki/Tracing_garbage_collection
过程如上图所示。
这个算法可以实现 "on-the-fly",也就是在程序执行的同时进行收集,并不需要暂停整个程序。
但是也会有一个缺陷,可能程序中的垃圾产生的速度会大于垃圾收集的速度,这样会导致程序中的垃圾越来越多无法被收集掉。
使用这种算法的是 Go 1.5、Go 1.6。
4. 分代收集
分代收集也是传统 Mark-Sweep 的一个改进。这个算法是基于一个经验:绝大多数对象的生命周期都很短。所以按照对象的生命周期长短来进行分代。
一般 GC 都会分三代,在 java 中称之为新生代(Young Generation)、年老代(Tenured Generation)和永久代(Permanent Generation);在 .NET 中称之为第 0 代、第 1 代和第2代。
原理如下:
- 新对象放入第 0 代
- 当内存用量超过一个较小的阈值时,触发 0 代收集
- 第 0 代幸存的对象(未被收集)放入第 1 代
- 只有当内存用量超过一个较高的阈值时,才会触发 1 代收集
- 2 代同理
因为 0 代中的对象十分少,所以每次收集时遍历都会非常快(比 1 代收集快几个数量级)。只有内存消耗过于大的时候才会触发较慢的 1 代和 2 代收集。
因此,分代收集是目前比较好的垃圾回收方式。使用的语言(平台)有 jvm、.NET 。
golang 的 GC
go 语言在 1.3 以前,使用的是比较蠢的传统 Mark-Sweep 算法。
1.3 版本进行了一下改进,把 Sweep 改为了并行操作。
1.5 版本进行了较大改进,使用了三色标记算法。go 1.5 在源码中的解释是“非分代的、非移动的、并发的、三色的标记清除垃圾收集器”
go 除了标准的三色收集以外,还有一个辅助回收功能,防止垃圾产生过快手机不过来的情况。这部分代码在 runtime.gcAssistAlloc 中。
但是 golang 并没有分代收集,所以对于巨量的小对象还是很苦手的,会导致整个 mark 过程十分长,在某些极端情况下,甚至会导致 GC 线程占据 50% 以上的 CPU。
因此,当程序由于高并发等原因造成大量小对象的gc问题时,最好可以使用 sync.Pool 等对象池技术,避免大量小对象加大 GC 压力。
go采用三色标记和写屏障:
- 起初所有的对象都是白色
- 扫描找出所有可达对象,标记为灰色,放入待处理队列
- 从队列提取灰色对象,将其引用对象标记为灰色放入队列
- 写屏障监视对象的内存修改,重新标色或放回队列
关于go的写屏障(write barrier),可以阅读最近一篇比较热的文章《Proposal: Eliminate STW stack re-scanning》。 作者主要介绍下个版本Go为了消除STW所做的一些改进,包括写屏障的优化方式。
并发的三色标记算法是一个经典算法,通过write barrier,维护”黑色对象不能引用白色对象”这条约束,就可以保证程序的正确性。Go1.5会在标记阶段开启write barrier。在这个阶段里,如果用户代码想要执行操作,修改一个黑色对象去引用白色对象,则write barrier代码直接将该白色对象置为灰色。去读源代码实现的时候,有一个很小的细节:原版的算法中只是黑色引用白色则需要将白色标记,而Go1.5实现中是不管黑色/灰色/白色对象,只要引用了白色对象,就将这个白色对象标记。这么做的原因是,Go的标记位图跟对象本身的内存是在不同的地方,无法原子性地进行修改,而采用一些线程同步的实现代价又较高,所以这里的算法做过一些变种的处理。
golang 垃圾回收机制的更多相关文章
- Golang垃圾回收机制(一)
原文: http://legendtkl.com/2017/04/28/golang-gc/ 1. Golang GC 发展 Golang 从第一个版本以来,GC 一直是大家诟病最多的.但是每一个版本 ...
- Golang垃圾回收机制(二)
原文:https://blog.csdn.net/qq_15427331/article/details/54613635 Go语言正在构建的垃圾收集器(GC),似乎并不像宣传中那样的,技术上迎来了巨 ...
- golang垃圾回收机制
golang的GC,1.8通过混合写⼊屏障, 使得STW降到了sub ms.go语言中程序代码执行和垃圾回收是并发执行的. 当前Go GC特征 :三色标记,并发标记和清扫,非分代,非紧缩,混合写屏障. ...
- 【GoLang】golang垃圾回收 & 性能调优
golang垃圾回收 & 性能调优 参考资料: 如何监控 golang 程序的垃圾回收_Go语言_第七城市 golang的垃圾回收(GC)机制 - 两只羊的博客 - 博客频道 - CSDN.N ...
- golang垃圾回收
常见GC算法 我总结了一下常见的 GC 算法.分别是:引用计数法.Mark-Sweep法.三色标记法.分代收集法. 1. 引用计数法 原理是在每个对象内部维护一个整数值,叫做这个对象的引用计数,当对象 ...
- Python-变量、变量作用域、垃圾回收机制原理-global nonlocal
变量实现原理决定了Python使用的垃圾回收机制为变量引用计数,当这个对象引用计数为0时候,则会自动执行__del__函数回收资源, del方法只是把变量指向的对象引用计数减一而已并删除这个变量 表达 ...
- .net垃圾回收机制编程调试试验
1. 什么是CLR GC? 它是一个基于引用跟踪和代的垃圾回收器. 从本质上,它为系统中所有活跃对象都实现了一种引用跟踪模式,如果一个对象没有任何引用指向它,那么这个对象就被认为是垃圾对象,并且可以被 ...
- JavaScript具有自动垃圾回收机制
JavaScript具有自动垃圾回收机制 原理: 找出那些不再继续使用的变量,然后释放其占用的内存. 正常的生命周期: 局部变量指在函数执行的过程中存在.而在这个过程中,会为局部变量在栈或 ...
- java垃圾回收机制
1 .垃圾回收机制(GC)垃圾回收就是回收内存中不再使用对象:(1)垃圾回收的步骤:1)查找内存中不再使用的对象:2)释放这些对象所占用的内存:(2)查找内存中不再使用的对象方法:1)引用计数法如果一 ...
随机推荐
- Material Designer的低版本兼容实现(六)—— Ripple Layout
新版的Android5.0添加了涟漪效果,虽然开源的库提供了各种控件便于大家使用涟漪效果.但是仍旧不可能满足所有需求,因此我今天改出来一个类叫做,LayoutRipple,其实感觉跟应该叫Ripple ...
- Linux常用命令汇总 - Linux Shell Cheat Sheet
1. 查看Linux操作系统信息: uname -a cat /proc/version lsb_release -a 2. 设置ls显示的文件夹的颜色(将下面这条目录加在 .bashrc 文件最后) ...
- IaaS,SaaS,PaaS的区别
你一定听说过云计算中的三个“高大上”的你一定听说过云计算中的三个“高大上”的概念:IaaS.PaaS和SaaS,这几个术语并不好理解.不过,如果你是个吃货,还喜欢披萨,这个问题就好解决了!好吧 ...
- 洛谷P1284 三角形牧场
题目描述 和所有人一样,奶牛喜欢变化.它们正在设想新造型的牧场.奶牛建筑师Hei想建造围有漂亮白色栅栏的三角形牧场.她拥有N(3≤N≤40)块木板,每块的长度Li(1≤Li≤40)都是整数,她想用所有 ...
- win32多线程-新版本MtVerify.h
api调用错误诊断宏,对GetLastError()函数的封装,并解析错误 从网上找的版本并进行了部分修改 /* * MtVerify.h * * The function PrintError() ...
- 排序算法的实现(归并,快排,堆排,希尔排序 O(N*log(N)))
今天跟着左老师的视频,理解了四种复杂度为 O(N*log(N))的排序算法,以前也理解过过程,今天根据实际的代码,感觉基本的算法还是很简单的,只是自己写的时候可能一些边界条件,循环控制条件把握不好. ...
- ISP图像处理&&相机系统
如何理解 ISO.快门.光圈.曝光这几个概念? 摄影基础篇——彻底弄清光圈.快门与ISO 理解这三个参数各自都是如何控制进入的光线量: 快门速度一般的表示方法是1/100s.1/30s.2s: 小的“ ...
- Redis学习手册(主从复制)(转)
一.Redis的Replication: 这里首先需要说明的是,在Redis中配置Master-Slave模式真是太简单了.相信在阅读完这篇Blog之后你也可以轻松做到.这里我们还是先列出一些理论性的 ...
- 详解vue组件的keep-alive
<keep-alive>是Vue的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM. <keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是 ...
- Centos curl ssl 替换 NSS 为 OpenSSL
参考:https://www.latoooo.com/xia_zhe_teng/368.htm 我的系统版本是 Centos 7 64位.为了方便,先安装常用的开发环境. yum groupinsta ...