Java GC为什么要分代

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

Java GC如何进行垃圾回收

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

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

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

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

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

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

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

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

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中的Eden,survivor 0以及survivor 1区。

Minor GC和Full GC的区别就是在这里,Minor GC只会回收Young Gen,并不会去收集Old Gen的垃圾。在Minor GC中,对于Old Gen指向Young Gen的引用也会被作为Minor GC的GC Roots的一部分。因此实际上Minor GC的GC Roots是比Full GC的GC 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,等以后再继续研究吧。

相关推荐
武子康4 分钟前
H3-World 为何在加载 LoRA 前,先检查导入的代码?
人工智能·深度学习·算法
SL_staff17 分钟前
私有化低代码不是妥协,而是企业级数字化的自主可控主航道
java
装不满的克莱因瓶23 分钟前
【SpringAI Alibaba】ReactAgent结构化输出OutputType与OutputSchema实战
java·人工智能·ai·springai·springaialibaba
秋天的怀念_David1 小时前
算法一千零一夜 · 第二十八夜 注册
java·程序人生·算法
Java码农也是农1 小时前
Spring 事务详解:@Transactional 如何生效、何时回滚、传播行为怎么选
java·spring boot·spring·事务
学长毕设2 小时前
基于SpringBoot的游乐园管理系统-附源码
java·spring boot·后端
SimonKing2 小时前
震惊:怎么回事,opencode一轮对话耗尽免费额度
java·后端·程序员
装不满的克莱因瓶2 小时前
【SpringAI Alibaba】ReactAgent响应SSE实时流式传输案例实战
java·人工智能·ai·sse·springai
杨凯凡2 小时前
Spring Boot 文件上传怎么做才安全?流式落盘、路径校验与数据库一致性实战
java·spring boot·文件上传·后端安全·数据库一致性
这个昵称不容易3 小时前
旅游管理系统的设计与实现(SpringBoot + MySQL + Vue 前后端分离)
java·springboot·毕设