万字概览 Java 虚拟机 (5)

可达性分析算法首先通过确定一些 GC Roots 根节点,然后跟着引用关系不断搜索对象,最终所有存活的对象形成一个图,凡是不属于这个图的节点的对象就是不可达的,会被 GC 回收掉。最常见的 GC Roots 有三种:

栈帧中的本地变量表上引用的对象;

类的 static 属性引用的对象;

方法区运行时常量池中的常量引用的对象。

垃圾回收算法

在 JVM 中最常见的 GC 算法有三种,分别是标记-清除算法、复制算法、标记-整理-清除算法。

标记-清除算法

这是最原始最基础的 GC 算法,之后的其他算法都是基于这个算法改进的。整个算法分两步,先标记出所有要回收的对象,第二步将标记好的对象直接回收。这种算法简单直接,但是会产生大量的内存碎片,最终可能因为总的剩余空间足够分配对象,但是却找不到连续的内存空间而提前进行一次垃圾回收。

复制算法

这种算法要求把内存空间等分为两块,每次只使用一块。对象标记完成后将未被标记的对象全部复制到未使用的另一块空间,然后将当期使用的这部分空间直接清空。由于复制时目标空间是干净的,所以复制也是在连续空间在进行,最终不会产生内存碎片。

这种算法的劣势是直接让可用内存减少了一半,如果对象存活率过高,复制效率会明显降低。目前几乎所有的 JVM 都采用这种算法来回收 Young 区,但基于 IBM 的一项研究,98% 的对象都会很快死亡,所以将内存区域按照 8:1:1 的比例分为了一个 Eden 区和 2 个 Survivor 区,从而克服了算法最初设计导致的内存使用效率低的问题。

为什么需要两个 Survivor 区

首先要明确 Survivor 区存在的意义是

避免 Old 区中存在太多生命周期很短的对象;

避免内存碎片化。

如果只有一个 Survivor 区,当 Eden 满了之后触发一次 YGC,存活对象被复制到 Survivor 中,下次 Eden 满了又触发 YGC 时问题就出现了。由于第二次 YGC 可能也回收了上次复制到 Survivor 区中的一部分对象,这时如果将本次 Eden 中存活的内存复制到 Survivor 区中就会出现内存碎片,而且碎片是存在已死亡对象的,这些对象无法被回收。

两个 Survivor 区的情况下,每次只使用一个区域,另一个区域就会是干净且连续的内存空间,这样就避免了内存碎片的出现。关于内存碎片带来的问题,我们在后面的 CMS GC 部分还会更细的聊到。

标记-整理-清除算法

这种算法也被叫做标记-整理算法,相比于标记-清除算法多了一个整理的过程。在标记完对象后,让存活对象都往一端移动,然后将端边界之外的内存都清空,这样就解决了标记-清除算法会产生大量内存碎片的问题。

虽然复制算法也解决了内存碎片问题,但是对于对象存活率非常高的内存区域而言,复制算法的效率比较低。

分代回收算法

分代回收算法并不是一种实际的算法,它是指对于 JVM Heap 中的不同内存区域采用不同的垃圾回收算法,从而整体提高 JVM 的内存使用效率和 GC 工作效率。

垃圾回收器

正如我们前文提到的「分代回收算法」,针对不同的内存区域,JVM 也提供了不同的垃圾回收器。Young 区常见的垃圾回收器有:

Serial GC
这是 JVM 以 Client 模式启动时默认的 GC,实现逻辑简单,单线程全程 STW 进行垃圾回收。这个 GC 不能发挥多核的优势,通常很少使用。但随着 Serverless 架构的兴起,Serial GC 又有了用武之地。

ParNew GC
这是 Serial GC 的多线程版本,可以通过 XX:ParallelGCThreads 参数设置参与 GC 的线程数。当 Old 区启动 CMS GC 时,将默认在 Young 区使用这个 GC。

PSGC
这是一款注重吞吐量的垃圾回收器,多用于后台计算类的系统。

Old 区常见的垃圾回收器有:

Serial Old
Serial GC 的 Old 区版本,主要作用是在 CMS 出现 CMF 时替代 CMS 进行 Full GC。

Parallel Old
PSGC 的 Old 区版本,同样注重吞吐量。

CMS
在 G1GC 出现之前使用最多的 Old 区 GC,它是一款几乎完全并发的垃圾回收器,注意不是完全并发。它采用标记-清除算法,随着应用程序的长时间运行会产生很严重的内存碎片问题。

除了上面的这些常见分代 GC 外,随着垃圾回收算法的不断改进,又出现了如 G1GC 这类混合式 GC。这类 GC 不再从物理上将 Heap 划分为 Young 区和 Old 区,而是仅从逻辑上进行划分,甚至 JDK11 提供的 ZGC 连逻辑上都不存在 Young 区和 Old 区了。

垃圾回收器的默认配对 # 配置 Young 区 Old 区 备注
1   +UseSerialGC   Serial   Serial Old   默认设置 Old 区 GC  
2   +UseParallelGC   PS   Parallel Old   默认同时设置 -XX:+UseParallelOldGC  
3   +UseConcMarkSweepGC   ParNew   CMS   默认同时设置 +UseParNewGC,且 Old 区使用 CMS 时,Young 区只能使用 ParNew  
4   +UseG1GC   G1   G1   G1下 Young 区和 Old 区变成了逻辑分区  
GC 术语

Young GC = Minor GC

Major GC = 对老年代和方法区的收回

Full GC = 对整个堆进行回收

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zygjjy.html