面试官:回家等通知吧 <一>

简介: 面试官:回家等通知吧 <一>

雇用制度对工人不利,但工人根本无力摆脱这个制度。——阮一峰


最近小黑被面试官问的得抑郁症了,我感觉她的情绪向不好的方向发展,因此我在微信上和她交流了下,最终发现问题所在。


事情是这样的,面试官问:go的垃圾回收了解多少,小黑回答:就那样,面试官说:回家等通知吧


这么简单的问题,小黑居然回答“就那样”,下面我就甩了一篇文章给她,让她三分钟看懂,看不懂打我。

1 业界常见垃圾回收算法



  • 引用计数(python/php)
  • 标记-清除(golang)
  • 分代回收(java)


2 说下Golang垃圾回收机制



2.1 垃圾回收原理


简单的说就是标记出内存中那些对象还在使用,那些对象未使用,把未使用的回收掉,以供后续内存分配使用。可是这么直白的说出来,小黑还是不懂,因此我直接甩图给她,看她还问我。

640.png

垃圾回收是从root对象开始扫描,把root对象引用的内存标记为”被引用”,考虑到内存块中存放的可能是指针,所以还需要递归的进行标记,全部标记完成后,只保留被标记的内存,未被标记的全部标识为未分配即完成了回收。


2.2 内存标记


内存分配有个span数据结构,span中维护了一个个内存块,并由一个位图allocBits表示每个内存块的分配情况。在span数据结构中还有另一个位图gcmarkBits用于标记内存块被引用情况。

640.png


如上图所示,allocBits记录了每块内存分配情况,而gcmarkBits记录了每块内存标记情况。标记阶段对每块内存进行标记,有对象引用的的内存标记为1(如图中灰色所示),没有引用到的保持默认为0.


allocBits和gcmarkBits数据结构是完全一样的,标记结束就是内存回收,回收时将allocBits指向gcmarkBits,则代表标记过的才是存活的,gcmarkBits则会在下次标记时重新分配内存,非常的巧妙。


2.3 三色标记法


前面介绍了对象标记状态的存储方式,还需要有一个标记队列来存放待标记的对象,可以简单想象成把对象从标记队列中取出,将对象的引用状态标记在span的gcmarkBits,把对象引用到的其他对象再放入队列中。


三色只是为了叙述上方便抽象出来的一种说法,实际上对象并没有颜色之分。这里的三色,对应了垃圾回收过程中对象的三种状态:


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


例如,当前内存中有A~F一共6个对象,根对象a,b本身为栈上分配的局部变量,根对象a、b分别引用了对象A、B, 而B对象又引用了对象D,则GC开始前各对象的状态如下图所示:


640.png

初始状态下所有对象都是白色的。

接着开始扫描根对象a、b:


640.png

由于根对象引用了对象A、B,那么A、B变为灰色对象,接下来就开始分析灰色对象,分析A时,A没有引用其他对象很快就转入黑色,B引用了D,则B转入黑色的同时还需要将D转为灰色,进行接下来的分析。如下图所示:

640.png


上图中灰色对象只有D,由于D没有引用其他对象,所以D转入黑色。标记过程结束:


640.png

最终,黑色的对象会被保留下来,白色对象会被回收掉。


2.4 STW


对于垃圾回收来说,回收过程中需要控制住内存的变化,否则回收过程中指针传递会引起内存引用关系变化,如果错误的回收了还在使用的内存,结果将是灾难性的。

Golang中的STW(Stop The World)就是停掉所有的goroutine,专心做垃圾回收,待垃圾回收结束后再恢复goroutine。

STW时间的长短直接影响了应用的执行,时间过长对于一些web应用来说是不可接受的,这也是广受诟病的原因之一。

为了缩短STW的时间,Golang不断优化垃圾回收算法,这种情况得到了很大的改善。


3 垃圾回收优化



3.1 写屏障(Write Barrier)


前面说过STW目的是防止GC扫描时内存变化而停掉goroutine,而写屏障就是让goroutine与GC同时运行的手段。虽然写屏障不能完全消除STW,但是可以大大减少STW的时间。

写屏障类似一种开关,在GC的特定时机开启,开启后指针传递时会把指针标记,即本轮不回收,下次GC时再确定。


GC过程中新分配的内存会被立即标记,用的并不是写屏障技术,也即GC过程中分配的内存不会在本轮GC中回收。


3.2 辅助GC(Mutator Assist)


为了防止内存分配过快,在GC执行过程中,如果goroutine需要分配内存,那么这个goroutine会参与一部分GC的工作,即帮助GC做一部分工作,这个机制叫作Mutator Assist。


4 垃圾回收触发时机



4.1 内存分配量达到阀值触发GC


每次内存分配时都会检查当前内存分配量是否已达到阀值,如果达到阀值则立即启动GC。

阀值 = 上次GC内存分配量 * 内存增长率


内存增长率由环境变量GOGC控制,默认为100,即每当内存扩大一倍时启动GC。


4.2 定期触发GC


默认情况下,最长2分钟出发一次GC,这个间隔在以下变量中被声明

src/runtime/proc.go:forcegcperiod:


var forcegcperiod int64 = 2 * 60 * 1e9


4.3 手动触发


程序代码中也可以使用runtime.GC()来手动触发GC。这主要用于GC性能测试和统计。


5 GC性能优化



GC性能与对象数量负相关,对象越多GC性能越差,对程序影响越大。

所以GC性能优化的思路之一就是减少对象分配个数,比如对象复用或使用大对象组合多个小对象等等。


另外,由于内存逃逸现象,有些隐式的内存分配也会产生,也有可能成为GC的负担。


6 关注公众号



微信公众号:堆栈future

相关文章
|
12月前
|
Go
Go中两个Nil可能不相等吗,面试官说回家等通知
Go中两个Nil可能不相等吗,面试官说回家等通知
|
6天前
|
算法 Java 调度
《面试专题-----经典高频面试题收集四》解锁 Java 面试的关键:深度解析并发编程进阶篇高频经典面试题(第四篇)
《面试专题-----经典高频面试题收集四》解锁 Java 面试的关键:深度解析并发编程进阶篇高频经典面试题(第四篇)
15 0
|
13天前
|
设计模式 SQL JavaScript
java面试宝典全套含答案
java面试宝典全套含答案
|
13天前
|
存储 Java
java面试题大全带答案_面试题库_java面试宝典2018
java面试题大全带答案_面试题库_java面试宝典2018
|
13天前
|
SQL 前端开发 Java
2019史上最全java面试题题库大全800题含答案(面试宝典)(4)
2019史上最全java面试题题库大全800题含答案(面试宝典)
|
13天前
|
SQL Java 数据库连接
2019史上最全java面试题题库大全800题含答案(面试宝典)(2)
2019史上最全java面试题题库大全800题含答案(面试宝典)
|
13天前
|
存储 设计模式 Java
java实习生面试题_java基础面试_java面试题2018及答案_java面试题库
java实习生面试题_java基础面试_java面试题2018及答案_java面试题库
|
13天前
|
SQL 算法 安全
java面试宝典_java基础面试_2018java面试题_2019java最新面试题
java面试宝典_java基础面试_2018java面试题_2019java最新面试题
|
13天前
|
算法 安全 网络协议
java高级面试题_java面试题大全带答案_线程面试题_java面试宝典2019
java高级面试题_java面试题大全带答案_线程面试题_java面试宝典2019
|
13天前
|
安全 算法 Java
java线程面试题_2019java面试题库
java线程面试题_2019java面试题库