Java GC为什么要分代

这几天研究了一下Java的GC,目前Java的垃圾回收算法已经非常的强大,停顿时间可以做的非常的短。之前有关注到在JEP 439: Generational ZGC中引入了分代ZGC,突然就想到一个问题,为什么GC需要对堆空间进行分代呢,有什么好处,接下来就结合网上的资料,简单聊一下自己的理解。

Java GC如何进行垃圾回收

首先,既然要实现垃圾回收,必然要知道哪些是垃圾,才可以进行删除。

Java采用的是可达性分析算法,通过一系列被称为GC Roots的引用为起点,从这些节点通过引用关系向下搜索,能够被遍历到的对象就判定为存活,其他的没有被遍历到的对象则被判定为垃圾。这里需要注意,可达性分析算法实际上并不是找出哪些对象是垃圾,而是找出所有的活对象,并将其他所有的对象判定为死对象然后清理

哪些引用可以被称之为GC Roots

  • 所有线程当前栈帧内指向堆里的引用,也就是当前正在被调用的方法里的局部变量

  • 本地方法栈内JNI引用的对象

  • Java方法区中的静态属性引用的对象

  • Java字符串常量池中的对象

  • JVM内部的一些引用,如Class对象,系统类加载器,一些异常对象如NullPointerExceptionOutOfMemoryError等。

Minor GC和Full GC

其实网上有一些人存在一些误解,就是Minor GC之所以比Full GC快,就是因为只扫描一部分GC Roots,实际上要实现可达性算法,最重要的就是要枚举出完整的GC Roots,否则可能会导致部分对象被漏掉导致GC错误删除了活对象,从而使程序出现问题。

这里贴一篇R大的回答,解释的非常清楚,下面的理解也是基于这篇回答来进行总结的java的gc为什么要分代? - 知乎

那么为什么要进行分代呢。

由于GC在整个工作过程中需要进行Stop-The-World,需要等待每一个线程都达到了safe point,然后开始进行垃圾回收,回收完成后程序再继续运行。那么GC最重要的指标,就是停顿时间

因此,缩短停顿时间思想实际上并不是缩小GC Roots的扫描范围,而是不进行所有堆空间的收集,转而只收集其中一部分。

基于这个理念,就出现了堆空间分区的方式,来实现部分收集。而分代就是其中一个实现思路。

分代基于这样一个假设,大部分对象的生命周期都很短,而生命周期长的对象一般都很长

基于这个假设,让新创建的对象都分配在一块很小的young gen里,然后频繁的对young gen进行收集,由于young gen很小,而且对象的存活时间很短。因此可以使用复制算法非常方便的进行收集,这样可以使GC的工作效率变得很高。比如HotSpot中的Edensurvivor 0以及survivor 1区。

Minor GCFull GC的区别就是在这里,Minor GC只会回收Young Gen,并不会去收集Old Gen的垃圾。在Minor GC中,对于Old Gen指向Young Gen的引用也会被作为Minor GCGC Roots的一部分。因此实际上Minor GCGC Roots是比Full GCGC Roots要大的。

Minor GC中,Old Gen中的所有对象都会被认为是活对象,那么在GC遍历的时候是怎么实现的呢,前面说了Minor GC只会收集Young Gen的堆空间,那么从GC Roots出发,遍历到的对象如果是Old Gen的,就不会往下遍历了。然而确实会存在Old Gen的对象引用到Young Gen的对象,因此将Old Gen作为GC Roots时,实际上是利用的Card Table来记录Old Gen对Young Gen引用的部分,在做Minor GC的时候就直接扫描Card Table记录下的Old Gen的区域,将这部分区域也作为GC Roots然后进行扫描。

Card Table(卡表)

关于Card Table(卡表)是什么前面已经介绍了,就是为了提高GC的效率,由于有Old Gen的对象可能会引用Young Gen的对象的可能性,在Minor GC中必须要把Old Gen也作为GC Roots,但是直接扫描整个老年代效率实在是太低了,因此出现了卡表,只需要记录下老年代对新生代的引用,在GC的时候,就可以直接把对应的老年代区域作为GC Roots,而不需要把整个老年代的对象都作为GC Roots。

在 HotSpot 的实现中,卡表是一个数组,数组的每一个元素对应着内存区域中一块固定大小为512字节的内存块,通过Write Barrier来维护Card Table的标记,在引用对象赋值的时候,将会检查对应的卡表是否已经被标记,如果没有,则会进行标记,被称为脏卡,之后就会作为Young GC的GC Roots进行扫描。

以上就是我个人对于GC的一些研究和记录,Java GC的黑科技还有很多,包括G1 GC的Region分区,ZGC实现最大暂停时间1ms,等以后再继续研究吧。

相关推荐
christine-rr5 分钟前
【25软考网工】第二章(8)差错控制、奇偶校验、CRC、海明码
网络·算法·网络工程师·软考·考试
寒也14 分钟前
识别法院PDF文件特定字段并插入数据库【正则表达式+本地化部署】
java·数据库·正则表达式·eclipse·pdf·达梦·ruoyi
想成为配环境大佬14 分钟前
LeetCode 打家劫舍+删除并获得点数
算法·leetcode·动态规划
中国lanwp25 分钟前
Spring Boot 版本与对应 JDK 版本兼容性
java·开发语言·spring boot
Chandler2425 分钟前
LeetCode:DFS综合练习
算法·leetcode·深度优先
懒虫虫~29 分钟前
Spring源码中关于抽象方法且是个空实现这样设计的思考
java·后端·spring
码银30 分钟前
【Java】接口interface学习
java·开发语言·学习
DKPT43 分钟前
重构之去除多余的if-else
java·开发语言·笔记·学习·面试
蓝黑202044 分钟前
Java如何在遍历集合时删除特定元素
java
掘金詹姆斯1 小时前
如何基于状态机对订单状态实现统一管理?
java·状态机