JVM 垃圾收集器

1. 概述

在本快速教程中,我们将演示不同JVM 垃圾回收 (GC) 实现的基础知识。然后,我们将学习如何在应用程序中启用特定类型的垃圾回收。

2. 垃圾回收简介

垃圾回收正如其名,从内存中查找并删除垃圾。但是,实际上,垃圾回收会跟踪 JVM 堆空间中可用的每个对象,并删除未使用的对象。

基本上,GC通过两个简单的步骤工作,称为标记和扫描:

  • 标记 – 这是垃圾回收器识别哪些内存正在使用中,哪些未在使用中的地方。
  • 扫描 – 此步骤删除在“标记”阶段标识的对象。

优势:

  • 无需手动内存分配/释放处理,因为未使用的内存空间由GC 自动处理
  • 没有处理悬空指针的开销
  • 自动内存泄漏管理(GC本身不能保证内存泄漏的完整证明解决方案;但是,它会处理其中的很大一部分)

弊:

  • 由于JVM必须跟踪对象引用的创建/删除,因此此活动需要比原始应用程序更多的 CPU 能力。它可能会影响需要大内存的请求的性能。
  • 程序员无法控制专用于释放不再需要的对象的 CPU 时间调度。
  • 使用某些 GC 实现可能会导致应用程序不可预测地停止。
  • 自动化内存管理不会像适当的手动内存分配/释放那样高效。

3. GC 实现

JVM有五种类型的GC实现:

  • 串行垃圾收集器
  • 并行垃圾收集器
  • CMS 垃圾回收器
  • G1 垃圾回收器
  • Z 垃圾收集器

3.1. 串行垃圾收集器

这是最简单的 GC 实现,因为它基本上适用于单个线程。因此,此GC实现在运行时会冻结所有应用程序线程。因此,在多线程应用程序(如服务器环境)中使用它不是一个好主意。

串行 GC 是大多数应用程序的首选垃圾回收器,这些应用程序没有小的暂停时间要求并在客户端样式的计算机上运行。要启用串行垃圾收集器,我们可以使用以下参数:

代码语言:javascript代码运行次数:0运行复制
java -XX:+UseSerialGC -jar Application.java

3.2. 并行垃圾收集器

它是JVM的默认GC,有时称为吞吐量收集器。与串行垃圾回收器不同,它使用多个线程来管理堆空间,但在执行GC 时也会冻结其他应用程序线程。

如果我们使用此GC,我们可以指定最大垃圾回收线程数和暂停时间、吞吐量和占用空间(堆大小)。

垃圾回收器线程的数量可以使用命令行选项-XX:ParallelGCThreads=<N>进行控制。

最大暂停时间目标(两个GC 之间的间隔 以毫秒为单位)使用命令行选项 XX:MaxGCPauseMillis=<N> 指定。

执行垃圾回收所花费的时间与在垃圾回收之外花费的时间称为最大吞吐量目标,可以通过命令行选项 XX:GCTimeRatio=<N> 指定。

最大堆占用量(程序运行时所需的堆内存量)是使用 xmx<N> 选项指定的。

要启用并行垃圾收集器,我们可以使用以下参数:

代码语言:javascript代码运行次数:0运行复制
java -XX:+UseParallelGC -jar Application.java

3.3. CMS 垃圾收集器

并发标记扫描 (CMS) 实现使用多个垃圾回收器线程进行垃圾回收。它专为喜欢较短垃圾回收暂停的应用程序而设计,并且可以在应用程序运行时与垃圾回收器共享处理器资源。

简而言之,使用此类 GC 的应用程序平均响应速度较慢,但不会停止响应以执行垃圾回收。

这里需要注意的一点是,由于此GC是并发的,因此调用显式垃圾回收(例如在并发进程工作时使用System.gc()将导致并发模式失败/中断

如果超过 98% 的总时间用于 CMS 垃圾回收,并且恢复的堆少于 2%,则CMS收集器会引发内存不足错误。如有必要,我们可以通过在命令行中添加选项 -XX:-UseGCOverheadLimit来禁用此功能。

此收集器还具有一种称为增量模式的模式,该模式在 Java SE 8 中已弃用,可能会在未来的主要发行版中删除。

要启用CMS垃圾收集器,我们可以使用以下标志:

代码语言:javascript代码运行次数:0运行复制
java -XX:+UseParNewGC -jar Application.java

从Java 9开始,CMS垃圾收集器已被弃用。因此,如果我们尝试使用它,JVM 会打印一条警告消息:

代码语言:javascript代码运行次数:0运行复制
>> 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支持:

代码语言:javascript代码运行次数:0运行复制
>> 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 垃圾收集器,我们可以使用以下参数:

代码语言:javascript代码运行次数:0运行复制
java -XX:+UseG1GC -jar Application.java

3.5. Java 8 更改

Java 8u20引入了另一个JVM参数,用于通过创建过多相同字符串的实例来减少不必要的内存使用这将通过将重复的字符串值删除到全局单个char[]数组来优化堆内存。

我们可以通过添加 -XX:+UseStringDecum作为JVM参数来启用此参数。

3.6. Z 垃圾收集器

ZGC(Z Garbage Collector)是一个可扩展的低延迟垃圾收集器,在Java 11中作为Linux的实验性选项首次亮相。_JDK_14在Windows和macOS操作系统下引入了_ZGC。ZGC从_Java 15开始获得生产状态。

ZGC并发执行所有昂贵的工作,无需停止应用程序线程的执行超过10毫秒,这使其适用于需要低延迟的应用程序。它使用带有彩色指针的负载屏障在线程运行时执行并发操作,并用于跟踪堆使用情况。

参考着色(彩色指针)是ZGC的核心概念。这意味着ZGC使用一些引用位(元数据位)来标记对象的状态。它还处理大小从 8MB 到 16TB 的堆。此外,暂停时间不会随着堆、活动集或根集大小的增加而增加。

G1 类似,Z 垃圾回收器对堆进行分区,只是堆区域可以具有不同的大小。

要启用Z 垃圾收集器,我们可以在低于 15 的JDK版本中使用以下参数:

代码语言:javascript代码运行次数:0运行复制
java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC Application.java

从版本 15 开始,我们不需要打开实验模式:

代码语言:javascript代码运行次数:0运行复制
java -XX:+UseZGC Application.java

我们应该注意,ZGC不是默认的垃圾收集器。

4. 结论

在本文中,我们研究了不同的JVM 垃圾回收实现及其用例。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。 原始发表:2023-02-15,如有侵权请联系 cloudcommunity@tencent 删除垃圾回收线程javajvm教程