JVM 垃圾收集器-Java快速进阶教程
1. 概述
在本快速教程中,我们将演示不同 JVM 垃圾回收 (GC) 实现的基础知识。然后,我们将学习如何在应用程序中启用特定类型的垃圾回收。
2. 垃圾回收简介
如同它的名称一样,垃圾回收似乎将处理从内存中查找和删除垃圾。但是,实际上,垃圾回收会跟踪 JVM 堆空间中可用的每个对象,并删除未使用的对象。
基本上,GC 通过两个简单的步骤工作,称为标记和扫描:
- 标记 – 这是垃圾回收器识别哪些内存正在使用中,哪些未在使用中的地方。
- 扫描 – 此步骤删除在“标记”阶段标识的对象。
优势:
弊:
- 由于 JVM 必须跟踪对象引用的创建/删除,因此此活动需要比原始应用程序更多的 CPU 能力。它可能会影响需要大内存的请求的性能。
- 程序员无法控制专用于释放不再需要的对象的 CPU 时间调度。
- 使用某些 GC 实现可能会导致应用程序不可预测地停止。
- 自动化内存管理不会像适当的手动内存分配/释放那样高效。
3. GC 实现
JVM有五种类型的GC实现:
- 串行垃圾收集器
- 并行垃圾收集器
- CMS 垃圾回收器
- G1 垃圾回收器
- Z 垃圾收集器
3.1. 串行垃圾收集器
这是最简单的 GC 实现,因为它基本上适用于单个线程。因此,此 GC 实现在运行时会冻结所有应用程序线程。因此,在多线程应用程序(如服务器环境)中使用它不是一个好主意。
然而,在QCon 2012上,Twitter工程师就Serial Garbage Collector的性能进行了精彩的演讲,这是更好地了解这个垃圾收集器的好方法。
串行 GC 是大多数应用程序的首选垃圾回收器,这些应用程序没有小的暂停时间要求并在客户端样式的计算机上运行。要启用串行垃圾收集器,我们可以使用以下参数:
java -XX:+UseSerialGC -jar Application.java
3.2. 并行垃圾收集器
它是 JVM 的默认 GC,有时称为吞吐量收集器。与串行垃圾回收器不同,它使用多个线程来管理堆空间,但在执行 GC 时也会冻结其他应用程序线程。
如果我们使用此 GC,我们可以指定最大垃圾收集线程数和暂停时间、吞吐量和占用空间(堆大小)。
垃圾回收器线程的数量可以使用命令行选项 -XX:ParallelGCThreads=<N> 进行控制。
最大暂停时间目标(两个 GC 之间的间隔 [以毫秒为单位])使用命令行选项 -XX:MaxGCPauseMillis=<N> 指定。
执行垃圾回收所花费的时间与在垃圾回收之外花费的时间称为最大吞吐量目标,可以通过命令行选项 -XX:GCTimeRatio=<N> 指定。
最大堆占用量(程序运行时所需的堆内存量)是使用 -Xmx<N> 选项指定的。
要启用并行垃圾收集器,我们可以使用以下参数:
java -XX:+UseParallelGC -jar Application.java
3.3. CMS 垃圾收集器
并发标记扫描 (CMS) 实现使用多个垃圾回收器线程进行垃圾回收。它专为喜欢较短垃圾回收暂停的应用程序而设计,并且可以在应用程序运行时与垃圾回收器共享处理器资源。
简而言之,使用此类 GC 的应用程序平均响应速度较慢,但不会停止响应以执行垃圾回收。
这里需要注意的一点是,由于此 GC 是并发的,因此调用显式垃圾收集(例如在并发进程工作时使用 System.gc())将导致并发模式失败/中断。
如果超过 98% 的总时间用于 CMS 垃圾回收,并且恢复的堆少于 2%,则 CMS 收集器会引发内存不足错误。如有必要,我们可以通过在命令行中添加选项 -XX:-UseGCOverheadLimit 来禁用此功能。
此收集器还具有一种称为增量模式的模式,该模式在 Java SE 8 中已弃用,可能会在未来的主要发行版中删除。
要启用CMS垃圾收集器,我们可以使用以下标志:
java -XX:+UseParNewGC -jar Application.java
从Java 9开始,CMS垃圾收集器已被弃用。因此,如果我们尝试使用它,JVM 会打印一条警告消息:
>> java -XX:+UseConcMarkSweepGC --version
Java HotSpot(TM) 64-Bit Server VM warning: Option UseConcMarkSweepGC was deprecated
in version 9.0 and will likely be removed in a future release.
java version "9.0.1"
此外,Java 14完全放弃了CMS支持:
>> java -XX:+UseConcMarkSweepGC --version
OpenJDK 64-Bit Server VM warning: Ignoring option UseConcMarkSweepGC;
support was removed in 14.0
openjdk 14 2020-03-17
3.4. G1 垃圾收集器
G1(垃圾优先)垃圾回收器专为在具有大内存空间的多处理器计算机上运行的应用程序而设计。它可从 JDK7 Update 4 和更高版本中获得。
G1 收集器将取代 CMS 收集器,因为它的性能效率更高。
与其他收集器不同,G1 收集器将堆分区为一组大小相等的堆区域,每个区域都是一个连续的虚拟内存范围。执行垃圾回收时,G1 显示并发全局标记阶段(即阶段 1,称为标记),以确定整个堆中对象的活动性。
标记阶段完成后,G1 知道哪些区域大部分是空的。它首先在这些区域收集,这通常会产生大量的可用空间(即阶段 2,称为扫描)。这就是为什么这种垃圾收集方法被称为垃圾优先。
要启用 G1 垃圾收集器,我们可以使用以下参数:
java -XX:+UseG1GC -jar Application.java
3.5. Java 8 更改
Java 8u20 引入了另一个 JVM 参数,用于通过创建过多相同字符串的实例来减少不必要的内存使用。这将通过将重复的字符串值删除到全局单个 char[] 数组来优化堆内存。
我们可以通过添加 -XX:+UseStringDecum 作为 JVM 参数来启用此参数。
3.6. Z 垃圾收集器
ZGC(Z 垃圾收集器)是一个可扩展的低延迟垃圾收集器,在 Java 11 中作为 Linux 的实验性选项首次亮相。JDK 14在Windows和macOS操作系统下引入了ZGC。ZGC从Java 15开始获得生产状态。
ZGC并发执行所有昂贵的工作,无需停止应用程序线程的执行超过10毫秒,这使其适用于需要低延迟的应用程序。它使用带有彩色指针的负载屏障在线程运行时执行并发操作,并用于跟踪堆使用情况。
参考着色(彩色指针)是ZGC的核心概念。这意味着ZGC使用一些引用位(元数据位)来标记对象的状态。它还处理大小从 8MB 到 16TB 的堆。此外,暂停时间不会随着堆、活动集或根集大小的增加而增加。
与 G1 类似,Z 垃圾回收器对堆进行分区,只是堆区域可以具有不同的大小。
要启用 Z 垃圾收集器,我们可以在低于 15 的 JDK 版本中使用以下参数:
java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC Application.java
从版本 15 开始,我们不需要打开实验模式:
java -XX:+UseZGC Application.java
我们应该注意,ZGC不是默认的垃圾收集器。