https://www.zhihu.com/question/403065438/answer/1300295527

作者:逃出黑洞的光线
链接:https://www.zhihu.com/question/403065438/answer/1426765597
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

首先要知道怎么标记垃圾(引用计数,根可达两种标记),再就是常用的垃圾回收算法(golang使用三色标记法,jvm使用分代回收法),然后关于写屏障有两种写屏障及过程(插入写屏障,删除写屏障),写屏障解决的问题(三色标记法错标或漏标问题)

最近不能上传图片,等过段时间吧

1.垃圾定位算法

(1)引用计数法

通常C++通过指针引用计数来回收对象,但是这不能处理循环引用,原理是在每个对象内部维护一个引用计数,当对象被引用时引用计数加一,当对象不被引用时引用计数减一。当引用计数为 0 时,自动销毁对象。

(2)根可达算法

从GC Roots向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链(即GC Roots到对象不可达)时,则证明此对象是不可用的,向JAVA、Go这种带有GC功能的高级语言使用的都是这种定位算法

简单来讲,从根对象往下查找引用,可以查找到的引用标记成可达,直到算法结束之后,没有被标记的对象就是不可达的,就会被GC回收。

2.垃圾回收算法

(1)标记-清除

(2)复制

(3)标记-压缩

以上三种算法是传统的垃圾回收算法,第一种容易产生内存碎片,第二种不会生成内存碎片,但是由于是整块复制,所以STW较长,效率太低,第三种是前两种的结合

(4)分代模型

JVM做垃圾回收时常用的GC算法,分为年轻代和老年代,年轻代使用复制算法,老年代使用标记压缩或者标记清除。

在分代模型中,年轻代的回收算法有ParNew、Serial、Parallel Scavenge,老年代的回收算法有CMS、Serial Old、Parallel Old,年轻代和老年代的回收算法一定是成对出现的,常见的回收对是ParNew-CMS、Serial-Serial Old、Parallel Scavenge-Parallel Old(jdk1.8默认)

另外jdk1.8可以用上面的分代模型,也可以使用不分代模型,即G1、ZGC等

(5)三色标记法

三色标记法是传统 Mark-Sweep 的一个改进,它是一个并发的 GC 算法。其实大部分的工作还是在标记垃圾,基本原理基于根可达

golang使用三色标记法来标记垃圾并回收

步骤:

a. 首先初始状态下所有对象都是白色的

b.从根对象开始遍历所有对象,将遍历到的对象从白色集合方放到灰色集合

c.遍历灰色集合中的对象将灰色对象引用的对象从白色集合放到灰色集合里面,此灰色对象放进黑色集合

d.重复c直到灰色集合为空

e.通过写屏障检测对象发生变化,重复上面操作

f.收集所有白色对象(垃圾)

3.三色标记算法标记垃圾会产生的问题

A对象已经标记并且引用的对象B也已经被标记,所以A放到黑色集合里,B对象被标记但是C对象还没标记,所以B是灰色

(1)浮动垃圾

如果B到C的引用断开,那么B找不到引用会被标黑,此时C就成了浮动垃圾,这种情况不碍事,大不了下次GC再收集

(2)漏标或者错标或者称作悬挂指针

但是如果此时用户goroutine新建对象A对对象C的引用,也就是从已经被标记成黑色的对象新建了引用指向了白色对象,因为A已经标黑,此时C将作为白色不可达对象被收集,这就出大问题了,程序里面A对象还正在引用C对象,但是GC把C对象看成垃圾给回收了,造成空指针异常。

4.写屏障

为了解决漏标的问题,需要使用写屏障,原理就是当A对象被标黑,此时A又引用C,就把C变灰入队

写屏障一定是在进行内存写操作之前执行的。

  • 强三色不变性 — 黑色对象不会指向白色对象,只会指向灰色对象或者黑色对象;
  • 弱三色不变性 — 黑色对象指向的白色对象必须包含一条从灰色对象经由多个白色对象的可达路径

Go 语言中使用两种写屏障技术,分别是 Dijkstra 提出的插入写屏障和 Yuasa 提出的删除写屏障。

(1)插入写屏障

# 伪代码
writePointer(slot, ptr):shade(ptr)*slot = ptr

上述插入写屏障的伪代码非常好理解,每当我们执行类似 *slot = ptr 的表达式时,我们会执行上述写屏障通过 shade 函数尝试改变指针的颜色。如果 ptr 指针是白色的,那么该函数会将该对象设置成灰色,其他情况则保持不变.

  1. 垃圾收集器将根对象指向 A 对象标记成黑色并将 A 对象指向的对象 B 标记成灰色;
  2. 用户程序修改 A 对象的指针,将原本指向 B 对象的指针指向 C 对象,这时触发写屏障将 C 对象标记成灰色;
  3. 垃圾收集器依次遍历程序中的其他灰色对象,将它们分别标记成黑色;

说人话,就是如果两个对象之间新建立引用,那么引用指向的对象就会被标记为灰色以满足强三色不变性,这是一种相对保守的屏障技术。

插入写屏障的缺点:

因为栈上的对象在垃圾收集中也会被认为是根对象,所以为了保证内存的安全,Dijkstra 必须为栈上的对象增加写屏障或者在标记阶段完成重新对栈上的对象进行扫描,这两种方法各有各的缺点,前者会大幅度增加写入指针的额外开销,后者重新扫描栈对象时需要暂停程序。

(2)删除写屏障

# 伪代码
writePointer(slot, ptr)shade(*slot)*slot = ptr

上述代码会在老对象的引用被删除时,将白色的老对象涂成灰色,这样删除写屏障就可以保证弱三色不变性,老对象引用的下游对象一定可以被灰色对象引用。

说人话,如果一个灰色对象指向一个白色对象的引用被删除,那么在删除之前写屏障检测到内存变化,就会把这个白色对象标灰。

总结

Go的垃圾回收官方形容为 非分代 非紧缩 写屏障 并发标记清理

非分代是golang GC区别于JVM GC分代模型的特点;非紧缩意味着在回收垃圾的过程中,不需要像复制算法那样移动内存中的对象,这样避免STW过长;标记清理算法的字面解释,就是将可达的内存块进行标记mark,最后没有标记的不可达内存块将进行清理sweep;Golang中实现标记功能的算法就是三色标记法,Golang里面三色标记法会造成错标问题,使用写屏障来解决这种问题,而JVM里面的CMS和G1解决错标或者漏标问题的算法分别是Increment Update和SATB

收发室柳大爷

公众号:茶理一事

GC就是Garbage Collection的缩写,翻译过来叫垃圾收集。所谓的垃圾,大多数情况下,指的是不再使用的内存。不收集会怎么样?会内存泄漏呗,房间就那么大,垃圾越来越多,可用空间就会越来越少。

几乎所有的编程语言,都有内存管理的办法。

只不过有的编程语言只提供了手动内存管理的方式,开发者必须自行对内存进行申请和释放。虽然手动管理相对精准,但是其实很麻烦,稍有不慎,就会造成内存泄漏、指针乱跑等各种奇奇怪怪的后果。

而类似Golang之类的编程语言,提供了垃圾收集的机制,开发者只需要了解垃圾收集的机制,大多数时间,并不需要手动管理内存。这为开发者提供了很大的便利,提升了工作效率。

但是,垃圾收集的工作机制并非完美,所以,在带来便利的同时,也会产生一些问题。总之,合理的利用垃圾收集带来的方便,避免垃圾收集影响业务,才是物尽其用。

拉几头驴,用来描述一下常见的垃圾收集机制:

1、引用计数。

谁想用驴干活的时候,就在驴身上画个圈圈,用一次画一个,用完了把代表本次使用的圈圈擦掉。当这头驴身上没圈圈的时候,就可以卸磨杀驴了,身上有圈圈的驴不能杀。

这个办法的优点是:用驴的人清楚自己用了哪头驴,一旦用驴的人忘了,当事驴自己也清楚。找不到主人,驴可以自己清除圈圈,洗白白再上路。

缺点也很明显:画圈圈也是体力活,需要时间;另一方面,驴皮就那么点面积,好几个圈圈画重叠了也是麻烦,再加上有可能圈圈套圈圈,驴也很惆怅啊。

2、标记清除。

想用某驴干活,就拍张这头驴的照片贴墙上,驴去干活就行了。驴干完活,去墙上把照片摘下来。有其他人用驴,也是拍照片贴墙上,以此类推。当墙上没有某头驴的照片时,这头驴就可以杀了吃肉了。

这个办法的优点是:驴没啥负担,只管干活;墙可以很宽,贴多少照片问题不大。

缺点是:找照片是个技术活,太笨的话,光是找照片就得很长时间。而且找照片的时候,最好别贴新的照片,不然就乱了,还得重新找。

3、分代收集。

驴多了不能放一起,短工驴放一个地方,长工驴放另一个地方。短工驴总是干活不休息,就升级成长工驴。杀驴的时候,一个人专杀干完短工的驴,短工驴不会摆资格,杀就杀了;另一个人专杀干完长工的驴,长工驴资格老,脾气暴,杀之前要点蚊香放音乐,搞点仪式感。

这个办法的优点是:非常驴性化,充分照顾了驴的感受;另一方面,多几个人杀驴,效率比较高。

缺点一样很明显:驴会升级,短工驴升级到长工驴,总是很矫情,两边杀手必须做好交接工作,不然有驴忘了杀就麻烦了。

---

Golang采用的是“标记清除”的杀驴方式,不对,是垃圾收集方式。因为这种方式关注点很简单,只要找驴照片足够快,其他都好办。

但是因为低版本的Golang,找驴照片的工作不熟练,再加上这种找驴方式本身的特点,就造成了Golang经常被人诟病的两个槽点:

1、Golang的GC速度慢。

这个在各个版本,一直是golang改进的重点。到目前为止(1.14),经过超级多版本的演进,特别是在1.14版本中实现了基于信号的真抢占式调度,目前的gc速度已经相当快了。不能跟纯手动内存管理比,只能说在同样使用gc机制的编程语言里,达到了应有的水平(之前确实有几个版本,达不到及格线)。

2、Golang的GC的STW(STOP THE WORLD)噩梦

前面提到过,找驴照片的时候,不能有新的照片贴墙上。这就造成了找驴的过程中,整个世界都停止了(其实有点夸张,毕竟只是照片墙不接受新照片,驴其实还在干活或者等死)。这个从根本上说,是机制问题。只要还用这个机制,那世界还是会时间停止。只不过,随着找驴速度的加快,这个时间裂缝,已经压缩到一个非常非常短的瞬间。当然,Golang本身的演进过程中,采用了很多细节技术,不但让这个STW的时间越来越短,而且还尽量的减少了这个停止世界的大小,把影响范围收窄。

任何GC的机制,都是有其优缺点的。

不能因为GC的弊端,就因噎废食。现在不使用GC的编程语言也有不少,比如C、比如Rust,效率确实高,也没有GC带来的问题。但还是那句话,有利就有弊,这些语言无一例外的是入门曲线很陡,内存管理需要专门花精力。

知其理、明其行、扬其长、避其短,所谓“君子生非异也,善假于物也”。

---

注:本回答只是简单说明一下Golang的垃圾收集机制。具体详细原理和代码解读,请参与专业文档。

Java和Go的GC差异相关推荐

  1. java.lang.OutOfMemoryError:GC overhead limit exceeded填坑心得

    该文章出自:http://www.cnblogs.com/hucn/p/3572384.html 分析工具:http://www.blogjava.net/jjshcc/archive/2014/03 ...

  2. Java 内存模型及GC原理

    一个优秀Java程序员,必须了解Java内存模型.GC工作原理,以及如何优化GC的性能.与GC进行有限的交互,有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率,才能 ...

  3. 【转】Java 内存模型及GC原理

    一个优秀Java程序员,必须了解Java内存模型.GC工作原理,以及如何优化GC的性能.与GC进行有限的交互,有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率,才能 ...

  4. Java垃圾回收(GC)机制详解

    Java垃圾回收(GC)机制详解 转自:https://www.cnblogs.com/xiaoxi/p/6486852.html 一.为什么需要垃圾回收 如果不进行垃圾回收,内存迟早都会被消耗空,因 ...

  5. java.lang.OutOfMemoryError:GC overhead limit exceeded

    我遇到这样的问题,本地部署时抛出异常java.lang.OutOfMemoryError:GC overhead limit exceeded导致服务起不来,查看日志发现加载了太多资源到内存,本地的性 ...

  6. Java 9 中的 GC 调优基础

    转载自   Java 9 中的 GC 调优基础 在经过了几次跳票之后,Java 9终于在原计划日期的整整一年之后发布了正式版.Java 9引入了很多新的特性,除了闪瞎眼的Module System和R ...

  7. Java中的低GC:使用原语而不是包装器

    总览 有两个很好的理由在可能的地方使用原语而不是包装器. 明晰. 通过使用原语,您可以清楚地知道null值是不合适的. 性能. 使用原语通常更快. 清晰度通常比性能更重要,并且是使用它们的最佳理由. ...

  8. 关于java垃圾回收器(GC)的一些基础知识

    关于java垃圾回收器(GC)的一些基础知识 定义: Java的垃圾回收机制是Java虚拟机提供的能力,用于在空闲时间以不定时的方式动态回收无任何引用的对象占据的内存空间. 注意:回收的是已经不再被使 ...

  9. 搞定java面试系列--jvm3 gc垃圾回收

    1.明确什么是垃圾 答案:在进行gc操作时候没有存活的对象. 那么怎么去判断对象是否存活: 老的方式-引用计数:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回 ...

最新文章

  1. 人工智能的浪潮中,知识图谱何去何从?
  2. 重磅!GitHub 日收 7000 星, Windows 计算器项目开源即爆红!
  3. 你知道event库吗?教你如何写一个自己的event库
  4. Java this关键字
  5. Mac终端使用技巧 切换到其他路径和目录
  6. 【MySql】Navicat Premium 15 无限试用脚本
  7. [深度学习] ImageAI库使用笔记
  8. java调用opencc将中文简体繁体转换
  9. OpenCV4.3 Java 编程入门:Core 组件中的数据结构与方法
  10. 双足机器人课设报告_小型舞蹈双足机器人的设计及实现
  11. java反序加密_对java程序加密防止反编译
  12. LintCode 627.最长回文串
  13. 瓷砖铺贴方法_15种瓷砖铺贴方式介绍 总有一种适合你
  14. css图片放大缩小动画
  15. [VishawaCTF]部分wp
  16. nginx进程模型,事件模型
  17. 标题 错误票据c语言,内含答案的 -- 2013蓝桥杯C语言本科组B.doc
  18. 五天学会绘画--像艺术家一样思考
  19. python中difference_python-ImageChops.difference的定义
  20. py基础系列(二):python语言基础(上)

热门文章

  1. 粘性定位(HTML、CSS)
  2. 实验4-1-8 求给定精度的简单交错序列部分和 (15 分)
  3. Mapbox词汇表中文文档(查找Mapbox相关的术语及其定义)
  4. 完整仿写鸿洋WanAndroid网站客户端
  5. Linux命令解释之rpm
  6. 距离度量以及python实现(一)
  7. 【李宏毅2020 ML/DL】P43-44 More about Adversarial Attack | Images Audio
  8. 随笔:大学英语六级 303 分到 576 分带来的一些回忆
  9. 编程语言的语法与语义
  10. 转 安装PHP出现make: *** [sapi/cli/php] Error 1 解决办法