微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

面试必备 JVM中垃圾回收用到的三色标记法

本文主要介绍Java虚拟机中三色标记法的具体应用。

也许读者在看本文之前,未曾听过三色标记,或者只是听过却不知道它的原理,三色标记法你可以理解为它只是一个方法,像go语言的gc和JVM的gc都用到了它,所以我们接下来要讨论一下,它是怎么用的,可以解决什么问题?

垃圾回收的大致过程

在JVM的一次垃圾回收过程中,首当其冲就是要找出已经不再使用的对象,如何判断对象不再使用了呢?Hotspot虚拟机就用的是可达性分析法(根搜索算法),以GcRoots对象集合为根结点,遍历整个对象图,凡是和GcRoots直接或间接可达的对象就将它们标记为存活。

如果是局部收集,如针对新生代的Minor GC,接下来就线性遍历新生代中的所有对象,对没有打上标记的对象进行回收。但这个时候的对象并不是非死不可,如果判定对象有必要(对象重写了finalize且未执行过)执行finalize方法的话,则将对象放到名为F-Queue的队列中等待Finalizer线程去执行,如果对象在finalize方法中成功拯救自己,则将对象从回收集合中移除出去。

三色标记就发生在以GcRoots为根遍历对象图,并标记存活对象的过程中。

有没有想过,如果堆区的对象特别多,那么遍历并标记对象所花的时间就越多,两者是成正比的,虚拟机的设计者也考虑到了这点,因此决不能让标记阶段完全地Stop The World(暂停用户线程),最好是让用户线程与GC线程并发工作,这样能够有更好的用户体验。

三色标记

下面开始介绍三色标记。三色是白色、黑色和灰色,每种颜色有不同的含义:

  • 白色:未被垃圾回收器访问过。即在第一次遍历之前所有对象都是白色,若在遍历之后还是白色,说明该对象到GcRoots不可达
  • 黑色:已被垃圾回收器访问过,且这个对象的所有引用都已经扫描过,因此黑色对象是安全存活的,也无须再扫描一遍了,黑色对象和白色对象中间正常情况下会隔着灰色对象,否则就是不正常的(用户线程并发修改
  • 灰色:已被垃圾回收器访问过,但这个对象上还有引用没被扫描

下面将主要讨论,在标记阶段,用户线程并发修改所导致的异常情况,以及异常情况如何解决!为什么要解决?因为我们的宗旨就是让用户线程与GC线程并发,从而大大降低用户线程的停顿时间。

如果用户线程在标记阶段停顿,即正常情况下三色标记是这样的:

在这里插入图片描述


1、初始状态,只有GcRoots是黑色的。

在这里插入图片描述


2、接下来在从左到右的扫描过程中,所有引用都访问过的对象标记为黑色,引用访问不全(比如有三个引用指向该对象,但只访问了其中一个引用)的对象标记为灰色,而未访问的对象仍然是白色。

在这里插入图片描述


3、最后,扫描结束,黑色对象就是到GcRoots可达的对象,而白色对象到GcRoots不可达。因此在清除阶段,就将白色对象回收即可。

当然,上面讲的是正常情况,如果用户线程在标记阶段并发修改了引用关系呢?会有两种异常情况:

  • 1、标记垃圾的对象,由于用户线程的修改错误标记成了存活,这种异常情况其实还好,顶多就是多了一些垃圾没有清除掉而已;
  • 2、标记成存活的对象,由于用户线程的修改错误标记成了垃圾,这个异常情况就比较严重了,想想你在程序中new了一个对象,用的好好地,结果一次gc之后给回收了,这不报空指针啊。

对于第2种异常情况,下面用图来说明一下。

在这里插入图片描述


图1,标记过程中,用户将灰色对象到白色对象的引用切断了,并建立了黑色对象到白色对象的引用,原本这个白色对象到GcRoots是可达的,但是这样修改之后,由于黑色对象不会再扫描第二遍,因此白色对象在标记结束后还是白色,最终被回收掉。

在这里插入图片描述


图2,也是一样,用户将右下角的灰色对象到白色对象的引用切断了,并建立了黑色对象到另一个白色对象的引用,原本下面两个白色对象到GcRoots是可达的,但是黑色对象不会再扫描第二遍了,下面两个白色对象在标记结束后还是白色,最终被回收掉。

增量更新与原始快照

我们发现,要想产生第2种异常情况,必须满足两个条件

  • 1、赋值器插入了一条或多条从黑色对象到白色对象的新引用
  • 2、赋值器删除了全部从灰色对象到该白色对象的直接或间接引用

要想避免第2种异常情况的发生,只要破坏这两个必要条件的其中一个即可。现在主流的垃圾回收器为了降低用户线程的停顿时间,已经采取了相应的解决方案:

  • 增量更新:破坏第一个条件;当用户新插入黑色到白色的新引用时,记录下来,等并发扫描结束后,再将记录的这些黑色对象作为根重新扫描一次,这样白色对象就会变成黑色从而保留下来;典型的垃圾回收器:CMS
  • 原始快照:破坏第二个条件;当用户删除灰色到白色的引用时,记录下来,等并发扫描结束后,再将记录的这些灰色对象作为根重新扫描一次,这样白色对象就会变成黑色从而保留下来;典型的垃圾回收器:G1

本文就写到这里。写本文的目的是想让读者get到虚拟机设计者在降低用户线程延迟方面用到了哪些设计,另一方面就是在面试的时候如果面试官问到这些不能让我们的脸上写满了懵逼二字,努力提升知识的广度与深度吧!

原文地址:https://www.jb51.cc/wenti/3286626.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐