全网最全JDK1~JDK15十一种JVM垃圾收集器的原理总结(下)

简介: 全网最全JDK1~JDK15十一种JVM垃圾收集器的原理总结(下)

12.png

CMS的缺点

吞吐量低

由于CMS在垃圾收集过程使用用户线程和GC线程并行执行,从而线程切换会有额外开销,因此CPU吞吐量就不如在GC过程中停止一切用户线程的方式来的高。

无法处理浮动垃圾,导致频繁Full GC

由于垃圾清除过程中,用户线程和GC线程并发执行,即用户线程仍在执行,则在执行过程中会产生垃圾,这些垃圾称为"浮动垃圾"。

如果CMS在垃圾清理过程中,用户线程需要在老年代中分配内存时发现空间不足,就需再次发起Full GC,而此时CMS正在进行清除工作,因此此时只能由Serial Old临时对老年代进行一次Full GC。

使用"标记-清除"算法产生碎片空间

由于CMS采用的是“标记-清除算法",因此产生大量的空间碎片,不利于空间利用率。为了解决这个问题,CMS可以通过配置

-XX:+UseCMSCompactAtFullCollection

强制JVM在FGC完成后対老年代进行压缩,执行一次空间碎片整理,但空间碎片整理阶段也会引发STW。为了减少STW次数,CMS还可以通过配置

-XX:+CMSFullGCsBeforeCompaction=n

在执行了n次FGC后,JVM再在老年代执行空间碎片整理。

在并发收集失败的情况下,Java虚拟机会使用其他两个压缩型垃圾回收器进行一次垃圾回收。由于G1的出现,CMS在Java 9中已被废弃。

三色标记算法 - 漏标问题引入

没有遍历到的 - 白色

自己标了,孩子也标了 - 黑色

自己标了,孩子还没标 - 灰色

第一种情况 ,已经标好了 ab,还没 d,如下,此时B=>D 消失,突然A=D了,因为 A已黑了,不会再 看他的孩子,于是 D 被漏标了!

12.png

13.png

14.png

漏标的解决方案

把 A 再标成灰色,看起来解决了?其实依然漏标!

CMS方案: Incremental Update的非常隐蔽的问题:

并发标记,依旧产生漏标!

15.png

16.png

于是产生了 G1!


G1(Garbage First)是一个横跨新生代和老年代的垃圾回收器。实际上,它已经打乱了前面所说的堆结构,直接将堆分成极其多个区域。每个区域都可以充当Eden区、Survivor区或者老年代中的一个。它采用的是标记-压缩算法,而且和CMS一样都能够在应用程序运行过程中并发地进行垃圾回收。


G1能够针对每个细分的区域来进行垃圾回收。在选择进行垃圾回收的区域时,它会优先回收死亡对象较多的区域。这也是G1名字的由来。


G1收集器(Garbage-First)

Hotspot 在JDK7中推出了新一代 G1 ( Garbage-First Garbage Collector )垃圾回收,通过

-XX:+UseG1GC

参数启用。和CMS相比,Gl具备压缩功能,能避免碎片向題,G1的暂停时间更加可控。性能总体还是非常不错的,G1是当今最前沿的垃圾收集器成果之一。

image.png

当今最前沿的垃圾收集器成果之一。

G1的特点

  • 追求停顿时间
  • 多线程GC
  • 面向服务端应用
  • 整体来看基于标记-整理和局部来看基于复制算法合并
    不会产生内存空间碎片.
  • 可对整个堆进行垃圾回收
  • 可预测的停顿时间

G1的内存模型

没有分代概念,而是将Java堆划分为一块块独立的大小相等的Region.当要进行垃圾收集时,首先估计每个Region中的垃圾数量,每次都从垃圾回收价值最大的Region开始回收,因此可以获得最大的回收效率.

image.png

G1将Java堆空间分割成了若干相同大小的区域,即region

包括

  • Eden
  • Survivor
  • Old
  • Humongous
  • 其中,Humongous是特殊的Old类型,专门放置大型对象.

这样的划分方式意味着不需要一个连续的内存空间管理对象.G1将空间分为多个区域,优先回收垃圾最多的区域.

G1采用的是Mark-Copy ,有非常好的空间整合能力,不会产生大量的空间碎片

G1的一大优势在于可预测的停顿时间,能够尽可能快地在指定时间内完成垃圾回收任务

在JDK11中,已经将G1设为默认垃圾回收器,通过jstat命令可以查看垃圾回收情况,在YGC时S0/S1并不会交换.

Remembered Set

一个对象和它内部所引用的对象可能不在同一个Region中,那么当垃圾回收时,是否需要扫描整个堆内存才能完整地进行一次可达性分析?

当然不是,每个Region都有一个Remembered Set,用于记录本区域中所有对象引用的对象所在的区域,从而在进行可达性分析时,只要在GC Roots中再加上Remembered Set即可防止对所有堆内存的遍历.

G1垃圾收集过程

初始标记

标记与GC Roots直接关联的对象,停止所有用户线程,只启动一条初始标记线程,这个过程很快。

并发标记

进行全面的可达性分析,开启一条并发标记线程与用户线程并行执行。这个过程比较长。

最终标记

标记出并发标记过程中用户线程新产生的垃圾,停止所有用户线程,并使用多条最终标记线程并行执行。

筛选回收

回收废弃的对象。此时也需要停止一切用户线程,并使用多条筛选回收线程并行执行。

image.png

S0/S1的功能由G1中的Survivor region来承载,通过GC日志可以观察到完整的垃圾回收过程如下,其中就有Survivor regions的区域从0个到1个

image.png

红色标识的为G1中的四种region,都处于Heap中.

G1执行时使用4个worker并发执行,在初始标记时,还是会触发STW,如第一步所示的Pause

回收算法

依旧前面例子:

image.png

image.png

因此,还是能追踪到 D,如果不维护 rset,需要扫描其他所有对象!因此只需要扫描该 region 即可~


针对新生代的垃圾回收器共有三个:Serial,Parallel Scavenge和Parallel New。这三个采用的都是标记-复制算法。其中,Serial是一个单线程的,Parallel New可以看成Serial的多线程版本。Parallel Scavenge和Parallel New类似,但更加注重吞吐率。此外,Parallel Scavenge不能与CMS一起使用。


针对老年代的垃圾回收器也有三个:刚刚提到的Serial Old和Parallel Old,以及CMS。Serial Old和Parallel Old都是标记-压缩算法。同样,前者是单线程的,而后者可以看成前者的多线程版本。


CMS采用的是标记-清除算法,并且是并发的。除了少数几个操作需要Stop-the-world之外,它可以在应用程序运行过程中进行垃圾回收。在并发收集失败的情况下,Java虚拟机会使用其他两个压缩型垃圾回收器进行一次垃圾回收。由于G1的出现,CMS在Java 9中已被废弃[3]。


G1(Garbage First)是一个横跨新生代和老年代的垃圾回收器。实际上,它已经打乱了前面所说的堆结构,直接将堆分成极其多个区域。每个区域都可以充当Eden区、Survivor区或者老年代中的一个。它采用的是标记-压缩算法,而且和CMS一样都能够在应用程序运行过程中并发地进行垃圾回收。


G1能够针对每个细分的区域来进行垃圾回收。在选择进行垃圾回收的区域时,它会优先回收死亡对象较多的区域。这也是G1名字的由来。


100g内存时,到头性能.

且G1 浪费空间,fullgc 特别慢!很多阶段都是 STW 的,所以有了 ZGC!

ZGC

听说你是 zerpo paused GC?

Java 11引入了ZGC,宣称暂停时间不超过10ms,支持 4TB,JDK13 到了 16TB!

和内存无关,TB 级也只停顿 1-10ms

image.png

UMA

image.png

  • NUMA
    知道NUMA存在并且能利用,哪个CPU要分配对象,优先分配离得近的内存
  • 目前不分代(将来可能分冷热对象)
    ZGC 学习 Asul 的商用C4收集器

颜色指针

image.png

原来的GC信息记录在哪里呢?对象头部

ZGC记录在指针,跟对象无关,因此可以immediate memory reuse

低42位指向对象,2^42=4T JDK13 2^44=16T, 目前最大就 16T,还能再大吗???

后面四位伐表对象不同状态m0 m1 remapped finalizable

18为unused

灵魂问题

内存中有个地址

地址中装了01001000 , mov 72,到底是一个立即数,还是一条指令?CPU->内存,通过总线连接,-> 数据总线地址总线控制总线,所以看是从啥总线来的即可

主板地址总线最宽 48bit 48-4 颜色位,就只剩 44 位了,所以最大 16T.


ZGC 阶段

1.pause mark start

2.concurrent mark

3.relocate

image.png

4.remap

对象的位置改变了,将其引用也改变过去 - 写屏障(与 JMM 的屏障不同,勿等同!)

而 ZGC 使用的读屏障!

GC的优势在哪里

  • 流行于现代的各大语言和平台
  • 效率和稳定性
  • 程序员不需要负责释放及销毁对象消除了不稳定性,延迟以及维护等几乎全部(普遍的)的可能
  • 保证了互操作性
  • 不需要与APIs之间交互的内存管理契约
  • 与不协调的库,框架,应用程序流畅地交互操作
目录
相关文章
|
2天前
|
自然语言处理 前端开发 Java
深入浅出JVM(六)之前端编译过程与语法糖原理
深入浅出JVM(六)之前端编译过程与语法糖原理
|
2天前
|
存储 算法 Java
深入浅出JVM(十八)之并发垃圾收集器G1
深入浅出JVM(十八)之并发垃圾收集器G1
|
2天前
|
存储 算法 Java
深入浅出JVM(十七)之并发垃圾收集器CMS
深入浅出JVM(十七)之并发垃圾收集器CMS
|
2天前
|
算法 Java
深入浅出JVM(十五)之垃圾收集器(上篇)
深入浅出JVM(十五)之垃圾收集器(上篇)
|
3天前
|
Arthas Prometheus 监控
JVM工作原理与实战(四十四):JVM常见题目
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了JVM常见题目等内容。
13 1
|
3天前
|
存储 监控 算法
JVM工作原理与实战(四十三):JVM常见题目
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了JVM常见题目等内容。
|
3天前
|
存储 监控 Oracle
JVM工作原理与实战(四十二):JVM常见题目
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了JVM常见题目等内容。
14 2
|
3天前
|
存储 监控 算法
JVM工作原理与实战(四十一):ShenandoahGC原理
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了ShenandoahGC、ShenandoahGC 1.0版本、ShenandoahGC 2.0版本、ShenandoahGC执行流程等内容。
|
1天前
|
IDE Java Shell
02|手把手教你安装JDK与配置主流IDE
02|手把手教你安装JDK与配置主流IDE
6 0
|
3天前
|
Java Shell 开发者
都2024年了!你还不知道在Docker中安装jdk?
都2024年了!你还不知道在Docker中安装jdk?