JVM中原始快照(Snapshot-At-The-Beginning)到底是如何实现的?

最近正在拜读《深入理解JVM》第3版,里面提到增量更新和原始快照的实现。对于原始快照的描述是这样的:“当灰色对象要删除指向白色对象的引用关系时,就将这个要删除的引用记录下来,在并发扫描结束之后,再将这些记录过的引用关系中的灰色对象为根,重新扫描一次。”
这说的啥?初读时的我一脸懵逼,确实,对初学者来说,这段文字很不容易理解,网上的解释也是七七八八,经过不懈努力,终于在一段教学视频中找到了一点权威线索。下面我们就来扒一扒原始快照的真实面目。
首先,我们先来回顾一下G1垃圾回收器重要特征region区:

  • region区

G1虽然和前辈们一样是分代垃圾回收器,但G1将堆划分为2048个region(大小为1~32M,2的幂次方),每个region从属不同的年代(注意:region并不固定属于某个年代,有时候属于young,有时候属于old,根据其保存对象来决定),每个年代都是一部分region的集合。G1从整体来看是基于“标记-整理”算法实现收集,从局部(两个Region)上来看是基于“复制”算法实现的,但无论如何,这两种算法都意味着G1运作期间不会产生内存空间碎片。region分区如图:
JVM中原始快照(Snapshot-At-The-Beginning)到底是如何实现的?_第1张图片

  • card(卡 )和card table (卡表)

每个region由若干个Card(512byte,card是堆内存最小可用粒度)构成,一个对象通常会占用一个region的若干个card,GC就是对region的card进行处理。

region的所有card记录在Card Table(byte[])中,通过byte[]下标保存card的地址,每个card默认未被引用,当一个card被引用时,值设为0。

  • Rset (Remembered Set)

每个Region都有一个Rset纪录其他region对本region的所有引用。通过扫描本region的RSet,来确定对region内的对象进行引用的对象是否存活,进而确定region内对象的存活情况。
Rset底层是Hash table,Key是region的起始地址,Value是Card Table卡表,卡表下标是card卡的地址,存放值为0表示被引用。

G1通过一个增量式的完全标记并发算法,计算region的活跃度,得到准确的region引用信息,不进行整堆扫描(整堆扫描效率低)。

写屏障(Write Barrier)
效果类似AOP的前后置通知(方法增强),eg:写前屏障pre-write barrier 。write barrier通过一定的性能开销来跟踪和记录对象及其引用,批处理更新到Rset中。
并发优化线程(Concurrence Refinement Threads)
写后屏障会将跨region的引用更新加入缓冲区,并发优化线程永远活跃,一旦发现全局列表有记录存在,就开始并发处理。

通过上面简单的介绍,相信大家对G1的region有了一些了解。现在我们来看看原始快照是如何运作的。

如图现在有ABC三个对象,A引用B,B引用C,经过三色标记后如图所示(这里涉及可达性分析)
JVM中原始快照(Snapshot-At-The-Beginning)到底是如何实现的?_第2张图片
此时B->C之间引用断裂,那么C为不可达对象,理所当然会被当作垃圾回收
JVM中原始快照(Snapshot-At-The-Beginning)到底是如何实现的?_第3张图片
但此时AC之间建立了新连接,因为三色标记算法,进行第二轮标记的时候,黑色对象是不会重新扫描的,那么问题就大了,此时C依旧被当作回收掉。那G1是如何保证并发的安全性的呢?

JVM中原始快照(Snapshot-At-The-Beginning)到底是如何实现的?_第4张图片
G1采用了原始快照算法(CMS采用增量更新实现)
JVM中原始快照(Snapshot-At-The-Beginning)到底是如何实现的?_第5张图片

============================================================
很久没上CSDN,不好意思。之前听了一些视频的一面之词,误导了大家。卡表和原始快照没关系,它只能为了解决跨代和跨区,去追溯了下hotspot源码,G1在GC的时候,存的是被删除的对象,为了防止再引用导致漏标,也就是直接将白色C置为灰色,本次GC不会回收 ,会等到下次GC回收,这样最多就是多了一些浮动垃圾,真正的快照是初始标记时的bitmap快照,并发标记中的快照只是往一个队列里面存oop。附上hotspot源码。前面的就不改了,当给大家排坑了
JVM中原始快照(Snapshot-At-The-Beginning)到底是如何实现的?_第6张图片

你可能感兴趣的:(深入了解JVM,java,jvm)