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

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

image.png

HotSpot虚拟机提供了多种垃圾收集器,每种收集器都有各自的特点,没有最好的垃圾收集器,只有最适合的垃圾收集器.我们可以根据自己实际的应用需求选择最适合的垃圾收集器.


使用分代垃圾收集器,基于以下观察事实(弱分代假设)


大多数分配对象的存活时间短

存活时间久的对象很少引用存活时间短的对象

由此, HotSpot VM 将堆分为两个物理区空间,这就是分代(永久代只存储元数据, eg. 类的数据结构,保留字符串( Interned String))


根据新生代和老年代各自的特点,我们应该分别为它们选择不同的收集器,以提升垃圾回收效率.

2.png

1 Serial

主要应用于Y-GC的垃圾回收器,采用串行单线程方式完成GC任务,其中“Stop The World"简称STW,即垃圾回收的某个阶段会暂停整个应用程序的执行

F-GC的时间相对较长,频繁FGC会严重影响应用程序的性能。

为何会有 STW

除了清除,还要做压缩。怎么才能标记和清除清楚上百万对象呢?

答案就是STW,让全世界停止下来。

Serial 回收流程

image.png

单线程 Stop-The-World 式,STW:工作线程全部停止。

4.png

5.png

单线程

只会使用一个CPU或一条GC线程进行垃圾回收,并且在垃圾回收过程中暂停其他所有的工作线程,从而用户的请求或图形化界面会出现卡顿.

适合Client模式

一般客户端应用所需内存较小,不会创建太多的对象,而且堆内存不大,因此垃圾回收时间比较短,即使在这段时间停止一切用户线程,也不会感到明显停顿.

简单高效

由于Serial收集器只有一条GC线程,避免了线程切换的开销.

采用"复制"算法

2 ParNew垃圾收集器

  • ParNew是Serial的多线程版本。
  • 6.png

2.1 多线程并行执行

ParNew由多条GC线程并行地进行垃圾清理.但清理过程仍然需要暂停一切其他用户线程.但由于有多条GC线程同时清理,清理速度比Serial有一定的提升.

2.2 适合多CPU的服务器环境

由于使用了多线程,因此适合CPU较多的服务器环境.

  • 与Serial性能对比
  • ParNew和Serial唯一区别就是使用了多线程进行垃圾回收,在多CPU的环境下性能比Serial会有一定程度的提升;但线程切换需要额外的开销,因此在单CPU环境中表现不如Serial,双CPU环境也不一定就比Serial高效.默认开启的收集线程数与CPU数量相同.

2.3 采用"复制"算法

2.4 追求"降低停顿时间"

和Serial相比,ParNew使用多线程的目的就是缩短垃圾收集时间,从而减少用户线程被停顿的时间.

3 Parallel Scavenge垃圾收集器

Parallel Scavenge和ParNew一样都是并行的多线程、新生代收集器,都使用"复制"算法进行垃圾回收.但它们有个巨大不同点:

ParNew收集器追求降低GC时用户线程的停顿时间,适合交互式应用,良好的反应速度提升用户体验.

Parallel Scavenge追求CPU吞吐量,能够在较短的时间内完成指定任务,因此适合不需要太多交互的后台运算.

吞吐量是指用户线程运行时间占CPU总时间的比例.

CPU总时间包括 : 用户线程运行时间 和 GC线程运行的时间.

因此,吞吐量越高表示用户线程运行时间越长,从而用户线程能够被快速处理完.


降低停顿时间的两种方式

1.在多CPU环境中使用多条GC线程,从而垃圾回收的时间减少,从而用户线程停顿的时间也减少;

2.实现GC线程与用户线程并发执行。所谓并发,就是用户线程与GC线程交替执行,从而每次停顿的时间会减少,用户感受到的停顿感降低,但线程之间不断切换意味着需要额外的开销,从而垃圾回收和用户线程的总时间将会延长。


Parallel Scavenge提供的参数


-XX:GCTimeRadio

直接设置吞吐量大小,GC时间占总时间比率.相当于是吞吐量的倒数.


-XX:MaxGCPauseMillis

设置最大GC停顿时间.

Parallel Scavenge会根据这个值的大小确定新生代的大小.如果这个值越小,新生代就会越小,从而收集器就能以较短的时间进行一次回收;但新生代变小后,回收的频率就会提高,吞吐量也降下来了,因此要合理控制这个值.


-XX:+UseAdaptiveSizePolicy

通过命令就能开启GC 自适应的调节策略(区别于ParNew).我们只要设置最大堆(-Xmx)和MaxGCPauseMillis或GCTimeRadio,收集器会自动调整新生代的大小、Eden和Survior的比例、对象进入老年代的年龄,以最大程度上接近我们设置的MaxGCPauseMillis或GCTimeRadio.


Parallel Scavenge不能与CMS一起使用。

以下都是老年代垃圾收集器

1 Serial Old垃圾收集器

Serial Old收集器是Serial的老年代版本,它们都是单线程收集器,也就是垃圾收集时只启动一条GC线程,因此都适合客户端应用.

它们唯一的区别就是Serial Old工作在老年代,使用"标记-整理"算法;而Serial工作在新生代,使用"复制"算法.

7.png

2 Parallel Old垃圾收集器

Parallel Old收集器是Parallel Scavenge的老年代版本,一般它们搭配使用,追求CPU吞吐量.

它们在垃圾收集时都是由多条GC线程并行执行,并暂停一切用户线程,使用"标记-整理"算法.因此,由于在GC过程中没有使垃圾收集和用户线程并行执行,因此它们是追求吞吐量的垃圾收集器.

8.png

9.png

3 CMS垃圾收集器(Concurrent Mark Sweep)

低延迟为先!

回收停顿时间比较短,对许多应用来说,快速响应比端到端的吞吐量更为重要。

管理新生代的方法与 parallel 和 serial 相同。在老年代则尽可能并发执行,每个 GC 周期只有2次短的停顿。


一种追求最短停顿时间的收集器,它在垃圾收集时使得用户线程和GC线程并发执行,因此在GC过程中用户也不会感受到明显卡顿。但用户线程和GC线程之间不停地切换会有额外的开销,因此垃圾回收总时间就会被延长。

垃圾回收过程

前两步需要"Stop The World"

初始标记

停止一切用户线程,仅使用一条初始标记线程对所有与GC Roots直接相关联的对象进行标记,速度很快,因为没啥根对象。

并发标记

使用多条并发标记线程并行执行,并与用户线程并发执行。

此过程进行可达性分析,标记出所有废弃的对象,速度很慢。

就像你麻麻在你屋子里收拾垃圾,并不影响你在屋里继续浪,这里也是新一代的收集器努力优化的地方。

重新标记

显然,你麻麻再怎么努力收垃圾,你的屋子可能还是一堆被你新生的垃圾,漏标了很多垃圾,所以此时必须 STW,停止一切用户线程!

使用多条重新标记线程并行执行,将刚才并发标记过程中新出现的废弃对象标记出来。这个过程的运行时间介于初始标记和并发标记之间。

并发清除

只使用一条并发清除线程,和用户线程们并发执行,清除刚才标记的对象。这个过程非常耗时。

10.png

线程角度

11.png

目录
相关文章
|
19天前
|
Rust 安全 Java
JVM原理与实现——Synchronized关键字
在多线程Java程序中,`Synchronized`关键字用于确保线程安全。本文深入探讨其工作原理,通过分析字节码`monitorenter`和`monitorexit`,解释JVM如何实现同步机制。文章展示了`Synchronized`方法的编译结果,并详细解析了轻量锁和重度锁的实现过程,包括Mark Word的状态变化及CAS操作的应用。最后简要介绍了`ObjectMonitor::enter()`函数在获取重度锁时的作用。
JVM原理与实现——Synchronized关键字
|
3月前
|
存储 监控 算法
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程 ?
尼恩提示: G1垃圾回收 原理非常重要, 是面试的重点, 大家一定要好好掌握
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程  ?
|
3月前
|
存储 监控 算法
JVM调优深度剖析:内存模型、垃圾收集、工具与实战
【10月更文挑战第9天】在Java开发领域,Java虚拟机(JVM)的性能调优是构建高性能、高并发系统不可或缺的一部分。作为一名资深架构师,深入理解JVM的内存模型、垃圾收集机制、调优工具及其实现原理,对于提升系统的整体性能和稳定性至关重要。本文将深入探讨这些内容,并提供针对单机几十万并发系统的JVM调优策略和Java代码示例。
69 2
|
3月前
|
缓存 Java 索引
查看并解析当前jdk的垃圾收集器
本文介绍了如何查看和解析当前JDK使用的垃圾收集器,通过在IDEA中配置JVM选项并运行示例代码来展示G1垃圾回收器的详细信息和命令行标志。
40 0
查看并解析当前jdk的垃圾收集器
|
3月前
|
前端开发 Java 应用服务中间件
JVM进阶调优系列(1)类加载器原理一文讲透
本文详细介绍了JVM类加载机制。首先解释了类加载器的概念及其工作原理,接着阐述了四种类型的类加载器:启动类加载器、扩展类加载器、应用类加载器及用户自定义类加载器。文中重点讲解了双亲委派机制,包括其优点和缺点,并探讨了打破这一机制的方法。最后,通过Tomcat的实际应用示例,展示了如何通过自定义类加载器打破双亲委派机制,实现应用间的隔离。
|
5月前
|
存储 算法 Java
JVM组成结构详解:类加载、运行时数据区、执行引擎与垃圾收集器的协同工作
【8月更文挑战第25天】Java虚拟机(JVM)是Java平台的核心,它使Java程序能在任何支持JVM的平台上运行。JVM包含复杂的结构,如类加载子系统、运行时数据区、执行引擎、本地库接口和垃圾收集器。例如,当运行含有第三方库的程序时,类加载子系统会加载必要的.class文件;运行时数据区管理程序数据,如对象实例存储在堆中;执行引擎执行字节码;本地库接口允许Java调用本地应用程序;垃圾收集器则负责清理不再使用的对象,防止内存泄漏。这些组件协同工作,确保了Java程序的高效运行。
42 3
|
5月前
|
C# UED 开发者
WPF打印功能实现秘籍:从页面到纸张,带你玩转WPF打印技术大揭秘!
【8月更文挑战第31天】在WPF应用开发中,打印功能至关重要,不仅能提升用户体验,还增强了应用的实用性。本文介绍WPF打印的基础概念与实现方法,涵盖页面元素打印、打印机设置及打印预览。通过具体案例,展示了如何利用`PrintDialog`和`PrintDocument`控件添加打印支持,并使用`PrinterSettings`类进行配置,最后通过`PrintPreviewWindow`实现打印预览功能。
566 0
|
5月前
|
C# UED 开发者
WPF动画大揭秘:掌握动画技巧,让你的界面动起来,告别枯燥与乏味!
【8月更文挑战第31天】在WPF应用开发中,动画能显著提升用户体验,使其更加生动有趣。本文将介绍WPF动画的基础知识和实现方法,包括平移、缩放、旋转等常见类型,并通过示例代码展示如何使用`DoubleAnimation`创建平移动画。此外,还将介绍动画触发器的使用,帮助开发者更好地控制动画效果,提升应用的吸引力。
282 0
|
4月前
|
Java
安装JDK18没有JRE环境的解决办法
安装JDK18没有JRE环境的解决办法
412 3
|
20天前
|
NoSQL 关系型数据库 MySQL
Linux安装jdk、mysql、redis
Linux安装jdk、mysql、redis
151 7