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,等以后再继续研究吧。

相关推荐
Omics Pro6 小时前
Cell封面|衰老生物学开源AI工具包
数据库·人工智能·算法·机器学习·自然语言处理
user_admin_god6 小时前
第 10 篇:拼上下文与生成——预算、边界与防幻觉
java·人工智能·spring boot·语言模型
用户3126874877206 小时前
GC 算法与垃圾收集器演进:从 Serial 到 ZGC
java
天天被压力6 小时前
【别再到处找免费股票数据API了:官方204个接口,32篇一次讲透 #02】涨停跌停与特色股池:5类股池接口一次拉全
java·人工智能·python
斯维赤6 小时前
从 @Tool 到 Agent 流水线:LangChain4j 进阶教学,一个库打全套
java·后端
SimonKing7 小时前
SSE项目`nexus-sse`持续优化,不一样的视觉效果
java·后端·程序员
斑鸠喳喳7 小时前
读写锁模式 Read-Write Lock
java·后端
信誓旦旦的程序猿7 小时前
【Python 量化取数指南 #09】Python 拿港股通数据:港股通成交与港股财报实测
java·python·股票数据api·股票数据·股票数据api接口·股票api数据接口·股票量化数据api
小程序设计8 小时前
本科毕设避坑|大数据 / 单片机物联网 / 网络工程 / PLC / 机械设计选题与落地心得
java·hive·hadoop·单片机
UIU1148 小时前
递归算法与汉诺塔
c++·学习·算法·c#·递归