Oracle:JVM & G1垃圾收集器
# 《G1垃圾收集器入门》
# Overview概述
# Purpose 目的
本教程涵盖了如何使用G1垃圾收集器的基础知识,以及如何将其与Hotspot JVM结合使用。您将学习G1收集器的内部工作原理、使用G1的关键命令行开关以及记录其操作的选项。
# Time to Complete 完成时间
Approximately 1 hour 大约 1 小时
# Introduction 介绍
本OBE涵盖了Java虚拟机(JVM)中G1垃圾收集(GC)的基础知识。在OBE的第一部分,提供了JVM的概述,并介绍了垃圾收集和性能。接下来,学生将复习CMS收集器如何与HotspotJVM协同工作。然后,提供了使用G1垃圾收集器与HotspotJVM时垃圾收集工作原理的逐步指南。随后,介绍了G1垃圾收集器可用的垃圾收集命令行选项。最后,您将学习如何使用G1收集器的日志记录选项。
# Hardware and Software Requirements硬件和软件要求
以下是硬件和软件要求列表:
运行 Windows XP 或更高版本、Mac OS X 或 Linux 的 PC。请注意,实际操作是在 Windows 7 上进行的,并未在所有平台上测试。然而,在 OS X 或 Linux 上应该都能正常运行。此外,建议使用多核机器。
Java 7 Update 9 或更高版本
最新的 Java 7 演示和示例 Zip 文件
# Java技术和JVM
# Java Overview Java概述
Java 是一种编程语言和计算平台,首次由 Sun Microsystems 于 1995 年发布。它是支持 Java 程序(包括实用程序、游戏和商业应用程序)的基础技术。Java 运行在全球超过 8.5 亿台个人电脑上,并在包括移动设备和电视设备在内的数十亿设备上运行。Java 由多个关键组件组成,这些组件共同构成了 Java 平台。
# Java Runtime Edition Java 运行时版本
当您下载 Java 时,您会获得 Java 运行时环境(JRE)。JRE 由 Java 虚拟机(JVM)、Java 平台核心类和支持 Java 平台的库组成。运行 Java 应用程序在您的计算机上需要这三个部分。使用 Java 7,Java 应用程序可以作为操作系统的桌面应用程序运行,作为桌面应用程序但通过 Java Web Start 从网络安装,或者作为浏览器中的 Web 嵌入式应用程序(使用 JavaFX)。
# Java Programming LanguageJava 编程语言
Java是一种面向对象的编程语言,具有以下特性。
平台独立性 - Java 应用程序被编译成字节码,存储在类文件中并加载到 JVM 中。由于应用程序在 JVM 中运行,它们可以在许多不同的操作系统和设备上运行。
面向对象 - Java 是一种面向对象的语言,继承了 C 和 C++ 的许多特性并进行了改进。
自动垃圾回收 - Java 自动分配和释放内存,因此程序不必承担这项任务。
丰富的标准库 - Java 包含大量预制对象,可用于执行输入/输出、网络和日期处理等任务。
# Java Development Kit Java 开发工具包
Java 开发工具包(JDK)是用于开发 Java 应用程序的工具集合。使用 JDK,您可以编译用 Java 编程语言编写的程序,并在 JVM 中运行它们。此外,JDK 还提供了打包和分发应用程序的工具。
JDK和JRE共享Java应用程序编程接口(JavaAPI)。JavaAPI是开发人员用来创建Java应用程序的预打包库集合。JavaAPI通过提供完成许多常见编程任务的工具,使开发变得更容易,包括字符串操作、日期/时间处理、网络和实现数据结构(例如,列表、映射、堆栈和队列)。
# Java Virtual Machine Java 虚拟机
Java虚拟机(JVM)是一种抽象的计算机。JVM是一个程序,对于在其中执行的程序来说,它看起来像一台机器。通过这种方式,Java程序被编写为使用相同的一组接口和库。每个特定操作系统的JVM实现将Java编程指令翻译为在本地操作系统上运行的指令和命令。这样,Java程序实现了平台独立性。
Java虚拟机的第一个原型实现由Sun Microsystems, Inc.完成,它在一个类似于当代个人数字助理(PDA)的手持设备上通过软件模拟了Java虚拟机指令集。Oracle目前的实现将Java虚拟机模拟在移动设备、桌面和服务器设备上,但Java虚拟机不假定任何特定的实现技术、主机硬件或主机操作系统。它本质上不是解释型的,也可以通过将其指令集编译为硅CPU的指令集来实现。它也可以通过微代码或直接在硅中实现。
Java虚拟机对Java编程语言一无所知,只了解一种特定的二进制格式,即类文件格式。一个类文件包含Java虚拟机指令(或字节码)和一个符号表,以及其他辅助信息。
为了安全起见,Java虚拟机对类文件中的代码施加了严格的语法和结构约束。然而,任何功能可以用有效类文件表示的语言都可以由Java虚拟机托管。由于被一个普遍可用的、与机器无关的平台所吸引,其他语言的实现者可以将Java虚拟机作为其语言的交付工具。
# 探索JVM架构
# Hotspot Architecture Hotspot 架构
HotSpot JVM 拥有一种架构,支持强大的功能和能力,并支持实现高性能和大规模扩展。例如,HotSpot JVM JIT 编译器生成动态优化。换句话说,它们在 Java 应用程序运行时做出优化决策,并生成针对底层系统架构的高性能本机机器指令。此外,通过其运行时环境和多线程垃圾收集器的成熟演变和持续工程,HotSpot JVM 即使在最大可用计算机系统上也能实现高扩展性。
HotSpot JVM: Architecture
JVM的主要组件包括类加载器、运行时数据区和执行引擎。
# Key Hotspot Components 关键的 Hotspot 组件
与性能相关的 JVM 关键组件在下图中突出显示。
Key HotSpot JVM Components
JVM 有三个组件在调整性能时需要关注。堆是存储对象数据的地方,这个区域由启动时选择的垃圾收集器管理。大多数调整选项与堆的大小和为您的情况选择最合适的垃圾收集器有关。JIT 编译器对性能也有很大影响,但在较新的 JVM 版本中很少需要调整。
# Performance Basics 性能基础
通常,在调整 Java 应用程序时,重点是两个主要目标之一:响应性或吞吐量。随着教程的进行,我们将回到这些概念。
# Responsiveness 响应性
响应能力指的是应用程序或系统对请求的数据的响应速度。示例包括:
- 桌面用户界面对事件的响应速度
- 网站返回页面的速度
- 数据库查询返回的速度
对于注重响应速度的应用程序,大的暂停时间是不可接受的。重点在于在短时间内做出响应。
# Throughput 吞吐量
吞吐量关注的是在特定时间内最大化应用程序的工作量。衡量吞吐量的示例包括:
- 在给定时间内完成的交易数量。
- 一个批处理程序在一小时内可以完成的作业数量。
- 一小时内可以完成的数据库查询数量。
对于注重吞吐量的应用程序来说,高暂停时间是可以接受的。由于高吞吐量应用程序关注的是较长时间段内的基准测试,因此快速响应时间并不是一个考虑因素。
# G1垃圾收集器
垃圾优先(G1)收集器是一种服务器风格的垃圾收集器,针对具有大内存的多处理器机器。它在实现高吞吐量的同时,以高概率满足垃圾收集(GC)暂停时间的目标。Oracle JDK 7 update 4 及更高版本全面支持 G1 垃圾收集器。
G1 收集器适用于以下应用程序:
可以像 CMS 收集器一样与应用程序线程并发运行。
在不引起长时间GC暂停的情况下压缩空闲空间。
需要更可预测的 GC 暂停时间。
不想牺牲大量的吞吐性能。
不需要更大的Java堆。
G1计划作为并发标记清除收集器(CMS)的长期替代品。将G1与CMS进行比较,有一些差异使G1成为更好的解决方案。其中一个差异是G1是一个压缩收集器。G1充分压缩以完全避免使用细粒度的空闲列表进行分配,而是依赖于区域。这大大简化了收集器的部分工作,并且基本上消除了潜在的碎片化问题。此外,G1比CMS收集器提供了更可预测的垃圾收集暂停,并允许用户指定所需的暂停目标。
# G1 操作概述
较旧的垃圾收集器(串行、并行、CMS)都将堆结构划分为三个部分:年轻代、老年代和固定内存大小的永久代。
Hotspot Heap Structure
所有内存对象最终都会进入这三个部分之一。
G1收集器采用了一种不同的方法。

堆被划分为一组大小相等的堆区域,每个区域都是一段连续的虚拟内存。某些区域集合被分配了与旧收集器相同的角色(伊甸园、幸存者、老年代),但它们没有固定的大小。这为内存使用提供了更大的灵活性。
在执行垃圾回收时,G1的操作方式类似于CMS回收器。G1执行并发的全局标记阶段,以确定整个堆中对象的存活性。在标记阶段完成后,G1知道哪些区域大多是空的。它首先在这些区域进行回收,这通常会产生大量的空闲空间。这就是为什么这种垃圾回收方法被称为“垃圾优先”。顾名思义,G1将其回收和压缩活动集中在堆中可能充满可回收对象(即垃圾)的区域。G1使用暂停预测模型来满足用户定义的暂停时间目标,并根据指定的暂停时间目标选择要回收的区域数量。
G1识别为适合回收的区域通过疏散进行垃圾回收。G1将对象从堆的一个或多个区域复制到堆的单个区域,在此过程中既压缩又释放内存。此疏散在多处理器上并行执行,以减少暂停时间并提高吞吐量。因此,在每次垃圾回收时,G1在用户定义的暂停时间内不断努力减少碎片化。这超出了之前方法的能力。CMS(并发标记清除)垃圾收集器不进行压缩。ParallelOld垃圾收集仅执行整个堆的压缩,这会导致相当长的暂停时间。
需要注意的是,G1并不是一个实时收集器。它在很大概率上能满足设定的暂停时间目标,但并不绝对保证。基于之前收集的数据,G1会估算在用户指定的目标时间内可以收集多少个区域。因此,收集器对收集区域的成本有一个相当准确的模型,并利用这个模型来确定在保持暂停时间目标的同时,应该收集哪些区域以及多少个区域。
注意:G1具有并发(与应用程序线程一起运行,例如,细化、标记、清理)和并行(多线程,例如,停止世界)阶段。Full GC仍然是单线程的,但如果调整得当,您的应用程序应该可以避免Full GC。
# G1 占用空间
- 如果您从 ParallelOldGC 或 CMS 收集器迁移到 G1,您可能会看到 JVM 进程大小增加。这很大程度上与“记账”数据结构(例如记忆集和收集集)有关。
- 记忆集(RSets) 跟踪指定区域中的对象引用。堆中每个区域对应一个 RSet。RSet 支持区域并行且独立的回收。RSet 的整体占用空间小于 5%。(它记录的是**“谁指向我(某 Region)”,但不是逐个对象**,而是记录其他 Region 中可能含有指向本 Region 的“卡片(card)位置”。这样在只回收一部分 Region 时,无需全堆扫描就能找到跨区入向引用。)
- 收集集(CSets) 是指将在 GC 中被收集的区域的集合。在 GC 期间,CSet 中的所有活动数据都会被清除(复制/移动)。区域集可以是伊甸园区、幸存者区和/或老年代区。CSets 对 JVM 大小的影响不到 1%。(CSet(Collection Set)是在一次 GC 停顿开始前/开始时,由 G1 的策略根据暂停目标、收益预测与(在 Mixed 周期时)并发标记结果 预先选出来的 Region 集合;随后停顿期对这些 Region 做疏散/复制(Evacuation)。不是“扫描完毕后才得到名单”。)
# G1的推荐使用场景
The first focus of G1 is to provide a solution for users running applications that require large heaps with limited GC latency. This means heap sizes of around 6GB or larger, and stable and predictable pause time below 0.5 seconds.
G1 的首要目标是为运行需要大堆内存且 GC 延迟有限的应用程序的用户提供解决方案。这意味着堆内存大小约为 6GB 或更大,且稳定和可预测的暂停时间低于 0.5 秒。
如果应用程序具有以下一个或多个特征,那么目前使用 CMS 或 ParallelOldGC 垃圾收集器运行的应用程序切换到 G1 将会受益。
- 完整GC的持续时间过长或过于频繁。
- 对象分配率或提升率变化显著。
- 不希望出现长时间的垃圾回收或压缩暂停(超过0.5到1秒)
注意:如果您正在使用 CMS 或 ParallelOldGC,并且您的应用程序没有经历长时间的垃圾回收暂停,那么继续使用当前的收集器是可以的。使用最新的 JDK 并不要求更换为 G1 收集器。
# 使用 CMS 回顾 GC
# 回顾分代垃圾回收和 CMS
并发标记清除(CMS)收集器(也称为并发低暂停收集器)负责收集老年代。它试图通过与应用程序线程同时进行大部分垃圾收集工作来最小化垃圾收集导致的暂停。通常,并发低暂停收集器不会复制或压缩活动对象。垃圾收集是在不移动活动对象的情况下完成的。如果碎片化成为问题,请分配更大的堆。
注意:CMS收集器在年轻代上使用的算法与并行收集器相同。
# CMS 收集阶段
CMS收集器在堆的老年代执行以下阶段:
Phase 阶段 | Description 描述 |
---|---|
(1) Initial Mark 初始标记 (Stop the World Event) (全停事件) | 老年代中的对象被“标记”为可达,包括那些可能从年轻代可达的对象。暂停时间通常相对于小型收集暂停时间较短。 |
(2) Concurrent Marking 并发标记 | 在 Java 应用程序线程执行时,同时遍历老年代对象图以查找可达对象。从标记的对象开始扫描,并传递性地标记从根可达的所有对象。在并发阶段 2、3 和 5 期间,变异器正在执行,并且在这些阶段中在 CMS 代中分配的任何对象(包括提升的对象)都会立即被标记为存活。 |
(3) Remark 重新标记 (Stop the World Event) (全停事件) | 查找由于 Java 应用程序线程在并发收集器完成对对象的跟踪后对对象进行更新而在并发标记阶段遗漏的对象。 |
(4) Concurrent Sweep 并发清除 | 收集在标记阶段被识别为不可达的对象。收集一个无用对象会将该对象的空间添加到空闲列表中以供后续分配。在此阶段可能会合并无用对象。请注意,活动对象不会被移动。 |
(5) Resetting 重置 | 通过清除数据结构为下一次并发收集做准备。 |
# 回顾垃圾收集步骤
接下来,让我们一步步回顾CMS收集器的操作。
# 1. CMS收集器的堆结构
堆被分为三个空间。

年轻代被分为 Eden 区和两个幸存者区。老年代是一个连续的空间。对象收集是在原地进行的。除非进行Full GC(Serial old),否则不会进行压缩。
# 2. CMS中年轻代GC的工作原理
年轻代被标记为浅绿色,老年代为蓝色。这是 CMS 在您的应用程序运行一段时间后可能的样子。对象散布在老年代区域。

使用 CMS 时,老年代对象就地释放。它们不会被移动。除非进行完整的 GC,否则空间不会被压缩。
# 3. Young Generation Collection年轻代收集
活动对象从 Eden 区和幸存者区复制到另一个幸存者区。任何达到老化阈值的较旧对象都会被提升到老年代。

# 4. After Young GC 年轻代 GC 后
在年轻代 GC 之后,Eden 区被清空,其中一个幸存者区也被清空。

在图中,新晋升的对象以深蓝色显示。绿色对象是尚未晋升到老年代的存活年轻代对象。
# 5. Old Generation Collection with CMS 使用 CMS 进行老年代收集
会发生两个“停止世界”事件:初始标记和重新标记。当老年代达到一定的占用率时,CMS 就会启动。

# 6. Old Generation Collection - Concurrent Sweep 老年代收集-并发清理
在前一个阶段未被标记的对象会被就地释放。没有压缩。

注意:未标记对象 $= =$ 死对象
# 7. Old Generation Collection - After Sweeping 老年代收集-清除后
在(4)清除阶段之后,你会发现大量内存已被释放。你还会注意到没有进行压缩。

最后,CMS收集器将进入(5)重置阶段,并等待下次达到GC阈值。
# G1垃圾收集器逐步指南
G1收集器在分配堆内存时采用了不同的方法。接下来的图片将逐步回顾G1系统。
# 1. G1 Heap Structure G1 堆结构
堆是一个内存区域,被分割成许多固定大小的区域。

区域大小由JVM在启动时选择。JVM通常目标是大约2000个区域,大小从1到32Mb不等。
# 2. G1 Heap Allocation G1 堆分配
实际上,这些区域被映射为 Eden、Survivor 和老年代空间的逻辑表示。

图片中的颜色显示了哪个区域与哪个角色相关联。活动对象从一个区域被转移(即复制或移动)到另一个区域。区域被设计为可以在不停止所有其他应用程序线程的情况下并行收集。
如图所示,区域可以分配到 Eden、Survivor 和老年代区域。此外,还有一种称为巨型区域的对象类型。这些区域被设计用于存放大小为标准区域 $50%$ 或更大的对象。它们被存储为一组连续的区域。最后一种区域类型是堆的未使用区域。
注意:在撰写本文时,收集巨型对象尚未优化。因此,您应避免创建此类大小的对象。
# 3. Young Generation in G1 G1 中的年轻代
堆被分成大约2000个区域。最小大小为1Mb,最大大小为32Mb。蓝色区域存放老年代对象,绿色区域存放年轻代对象。

请注意,这些区域不需要像旧的垃圾收集器那样是连续的。
# 4. A Young GC in G1 G1中的年轻代垃圾回收
存活对象被转移(即复制或移动)到一个或多个幸存者区域。如果达到老化阈值,一些对象会被提升到老年代区域。

这是一个全停顿(STW)暂停。伊甸园区大小和幸存者区大小是为下一个年轻代垃圾回收计算的。会保留核算信息以帮助计算大小。诸如暂停时间目标等因素会被考虑在内。
这种方法使得调整区域大小变得非常容易,可以根据需要增大或缩小。
# 5. End of a Young GC with G1 G1的年轻代GC结束
存活对象已被转移到幸存者区域或老年代区域。

最近晋升的对象显示为深蓝色。幸存者区域为绿色。
总之,可以对G1中的年轻代做如下总结:
堆是一个被分割成多个区域的单一内存空间。
年轻代内存由一组不连续的区域组成。这使得在需要时可以轻松调整大小。
年轻代垃圾回收,或称年轻代GC,是“停止世界”事件。所有应用程序线程都会在操作期间停止。
年轻代 GC 使用多个线程并行完成。
存活对象被复制到新的幸存者区域或老年代区域。
# 使用G1进行老年代收集
与 CMS 收集器类似,G1 收集器被设计为旧生代对象的低停顿收集器。下表描述了 G1 在旧生代的收集阶段。
# G1收集阶段-并发标记周期阶段
G1收集器在堆的老年代执行以下阶段。请注意,某些阶段是年轻代收集的一部分。
Phase 阶段 | Description 描述 |
---|---|
(1) Initial Mark 初始标记 *(Stop the World Event) * | 这是一个“停止世界”事件。在 G1 中,它附加在正常的年轻代 GC 上。标记可能引用老年代对象的幸存者区域(根区域)。 |
(2) Root Region Scanning 根区域扫描 | 扫描存活区以查找对老年代的引用。这是在应用程序继续运行时发生的。此阶段必须在年轻代垃圾回收发生之前完成。 |
(3) Concurrent Marking 并发标记 | 在整个堆中查找存活对象。这是在应用程序运行时发生的。此阶段可以被年轻代垃圾收集中断。 |
(4) Remark 标记 (Stop the World Event) | 完成堆中存活对象的标记。使用一种称为快照起始(SATB)的算法,这比 CMS 收集器使用的算法快得多。 |
(5) Cleanup 清理 (Stop the World Event and Concurrent) (全停事件和并发) | 1、对存活对象和完全空闲的区域进行计数。(Stop the world) 2、清理记忆集。(Stop the world) 3、重置空闲区域并将其返回到空闲列表。(并发) |
Copying 复制 *(Stop the World Event) * | 这些是停止世界的暂停,用于将存活对象转移或复制到新的未使用区域。这可以在年轻代区域中完成,记录为 [GC pause (young)] 。或者在年轻代和老年代区域中同时进行,记录为 [GC Pause (mixed)] 。 |
# G1 老年代收集逐步指南
在定义了这些阶段后,让我们看看它们如何与G1收集器中的老年代进行交互。
# 1. Initial Marking Phase 初始标记阶段
存活对象的初始标记附加在年轻代垃圾收集上。在日志中,这被记录为 GC pause (young) (initial-mark)。

# 2. Concurrent Marking Phase 并发标记阶段
如果发现空区域(用“X”表示),它们会在Remark阶段立即被移除。同时,决定存活性的“计算”信息也会被计算。

# 3. Remark Phase Remark 阶段
空区域被移除并回收。现在所有区域的存活性都已计算。

# 4. Copying/Cleanup Phase 复制/清理阶段
G1 选择“存活率”最低的区域,即那些可以最快收集的区域。然后这些区域与年轻代 GC 同时被收集。这在日志中表示为 [GC pause (mixed)]。因此,年轻代和老年代同时被收集。
Copying/Cleanup Phase
# 5. After Copying/Cleanup Phase 复制/清理阶段后
选定的区域已被收集并压缩到图中显示的深蓝色区域和深绿色区域。
After Copying/Cleanup Phase
# 老年代GC总结
In summary, there are a few key points we can make about the G1 garbage collection on the old generation.
总之,我们可以对旧生代的G1垃圾收集得出几个关键点。
并发标记阶段
- 在应用程序运行时,活动信息是并发计算的。
- 这些活动信息识别出在转移暂停期间最适合回收的区域。
- 在G1垃圾收集器中,没有像CMS那样的清扫阶段。
oRemark Phase 标记阶段
- 使用快照起始(SATB)算法,这比CMS使用的算法要快得多。
- 完全空的区域会被回收。
复制/清理阶段
- 年轻代和老年代同时被回收。
- 老年代区域是根据其存活率选择的。
# 命令行选项和最佳实践
在本节中,让我们看看G1的各种命令行选项。
# 基本命令行
要启用G1收集器,请使用:-XX:+UseG1GC
以下是启动 JDK 演示和示例下载中包含的 Java2Demo 的示例命令行:
java -Xmx50m -Xms50m -XX: +UseG1GC -XX:MaxGCPauseMillis=200 -jar
c:\javademos\demo\jfc\Java2D\Java2demo.jar
# 关键命令行开关
-XX: +UseG1GC - 告诉 JVM 使用 G1 垃圾收集器。
-XX:MaxGCPauseMillis=200 - 设置 GC 暂停时间的最大目标。这是一个软目标,JVM 将尽最大努力实现它。因此,有时暂停时间目标可能无法达到。默认值是 200 毫秒。
-XX:InitiatingHeapOccupancyPercent=45 - 启动并发 GC 周期的堆占用率百分比。G1 使用它来根据整个堆的占用情况触发并发 GC 周期,而不仅仅是某一代的占用情况。值为 0 表示“持续进行 GC 周期”。默认值为 45(即 $45%$ 已满或被占用)。
# 最佳实践
在使用G1时,您应该遵循一些最佳实践。
# 不要设置年轻代大小
通过-Xmn 显式设置年轻代大小会干扰 G1 收集器的默认行为。
- G1将不再遵循收集的暂停时间目标。因此,实质上,设置年轻代大小会禁用暂停时间目标。
- G1不再能够根据需要扩展和收缩年轻代空间。由于大小是固定的,因此无法对大小进行更改。
# 响应时间指标
与其使用平均响应时间(ART)作为设置 XX:MaxGCPauseMillis=
# 什么是撤离失败?
当 JVM 在 GC 期间由于幸存者和提升对象而耗尽堆区域时,会发生提升失败。堆无法扩展,因为它已经达到最大。这在使用 -XX: +PrintGCDetails 时由 to-space overflow 在 GC 日志中指示。这是昂贵的!
- GC仍然必须继续,因此必须释放空间。
未成功复制的对象必须在原地晋升。
对 CSet 中区域的 RSet 的任何更新都必须重新生成。
所有这些步骤都很耗费资源。
# 如何避免疏散失败
为了避免疏散失败,请考虑以下选项。
增加堆大小
- 增加-XX:G1ReservePercent=n,默认值是10。
- G1通过尝试保留备用内存以防需要更多“to-space”来创建一个虚假的上限。
提前开始标记周期
- 使用
-XX:ConcGCThreads=n
选项增加标记线程的数量。
# G1 GC 开关完整列表
这是G1GC开关的完整列表。请记得遵循上述最佳实践。
选项和默认值 | Description 描述 |
---|---|
-XX:+UseG1GC | 使用垃圾优先(G1)收集器 |
-XX:MaxGCPauseMillis=n | 设置最大 GC 暂停时间的目标。这是一个软目标,JVM 将尽最大努力实现它。 |
-XX:InitiatingHeapOccupancyPercent=n | 整个堆占用率以启动并发 GC 周期的百分比。它用于基于整个堆的占用率而不是仅某一代的占用率来触发并发 GC 周期的垃圾收集器(例如,G1)。值为 0 表示“持续进行 GC 周期”。默认值为 45。 |
-XX:NewRatio=n | 新生代/老年代大小的比例。默认值为 2。 |
-XX:SurvivorRatio=n | 伊甸园/幸存者空间大小的比例。默认值为 8。 |
-XX:MaxTenuringThreshold=n | 晋升阈值的最大值。默认值为 15。 |
-XX:ParallelGCThreads=n | 设置垃圾收集器并行阶段使用的线程数。默认值因 JVM 运行的平台而异。 |
-XX:ConcGCThreads=n | 并发垃圾收集器将使用的线程数量。默认值因 JVM 运行的平台而异。 |
-XX:G1ReservePercent=n | 设置保留的堆空间量作为虚假上限,以减少晋升失败的可能性。默认值为 10。 |
-XX:G1HeapRegionSize=n | 在 G1 中,Java 堆被细分为大小均匀的区域。这决定了各个子分区的大小。该参数的默认值是根据堆大小以人体工学方式确定的。最小值为 1Mb,最大值为 32Mb。 |
# 使用G1记录GC日志
我们需要讨论的最后一个主题是使用日志信息来分析 G1 收集器的性能。本节提供了一个关于可以用来收集数据的开关以及日志中打印的信息的快速概述。
# 设置日志详细程度
您可以将详细程度设置为三个不同的级别。
# (1)-verbosegc(相当于-XX:+PrintGC)将日志的详细程度设置为精细。
[GC pause (G1 Humongous Allocation) (young) (initial-mark) 24M- >21M(64M), 0.2349730 secs]
[GC pause (G1 Evacuation Pause) (mixed) 66M->21M(236M), 0.1625268 secs]
2
# (2) -XX:+PrintGCDetails 将详细程度设置为更精细。该选项显示以下信息:
- 每个阶段都会显示平均、最小和最大时间。
根扫描、RSet更新(包含已处理缓冲区信息)、RSet扫描、对象复制、终止(包含尝试次数)。
还显示“其他”时间,如选择 CSet、引用处理、引用入队和释放 CSet 所花费的时间。
显示 Eden、幸存者和总堆的占用情况。
[Ext Root Scanning (ms): Avg: 1.7 Min: 0.0 Max: 3.7 Diff: 3.7]
[Eden: 818M(818M)->0B(714M) Survivors: 0B->104M Heap: 836M(4096M)->409M(4096M)]
2
# (3) -XX:+UnlockExperimentalVMOptions -XX:G1LogLevel=finest 将详细级别设置为最精细。类似于更精细,但包括单个工作线程的信息。
[Ext Root Scanning (ms): 2.1 2.4 2.0 0.0
Avg: 1.6 Min: 0.0 Max: 2.4 Diff: 2.3]
[Update RS (ms): 0.4 0.2 0.4 0.0
Avg: 0.2 Min: 0.0 Max: 0.4 Diff: 0.4]
[Processed Buffers : 5 1 10 0
Sum: 16, Avg: 4, Min: 0, Max: 10, Diff: 10]
2
3
4
5
6
# Determining Time 确定时间
A couple of switches determine how time is displayed in the GC log.
有几个开关决定了在 GC 日志中如何显示时间。
# (1) -XX: +PrintGCTimeStamps - 显示自 JVM 启动以来的经过时间。
1.729: [GC pause (young) 46M->35M(1332M), 0.0310029 secs]
# (2) -XX:+PrintGCDateStamps - 为每个条目添加一个当天时间的前缀。
# 理解G1日志
为了理解日志,本节使用实际的GC日志输出定义了一些术语。以下示例展示了日志中的输出,并对您将在其中找到的术语和数值进行解释。
注意:欲了解更多信息,请查看 Poonam Bajaj 关于 G1 GC 日志的博客文章。
# G1日志术语索引
Clear CT 清除 CT (opens new window) CSet (opens new window) External Root Scanning 外部根扫描 (opens new window) Free CSet 释放 CSet (opens new window) GC Worker End GC 工作线程结束 (opens new window) GC Worker Other GC 工作线程其他 (opens new window) Object Copy 对象复制 (opens new window) Other 其他 (opens new window) Parallel Time 并行时间 (opens new window) Ref Eng 引用引擎 (opens new window) Ref Proc 引用过程 (opens new window) Scanning Remembered Sets 扫描记忆集 (opens new window) Termination Time 终止时间 (opens new window) Update Remembered Set 更新记忆集 (opens new window) Worker Start 工作线程启动 (opens new window)
Parallel Time 并行时间
414.557: [GC pause (young), 0.03039600 secs] [Parallel Time: 22.9 ms] [GC Worker Start (ms): 7096.0 7096.0 7096.1 7096.1 706.1 7096.1 7096.1 7096.1 7096.2 7096.2 7096.2 7096.2 Avg: 7096.1, Min: 7096.0, Max: 7096.2, Diff: 0.2]
并行时间 – 暂停的主要并行部分的总体耗时 工作线程启动 - 工作线程开始的时间戳 注意:日志按线程 ID 排序,并且每个条目都是一致的
External Root Scanning 外部根扫描
[Ext Root Scanning (ms): 3.1 3.4 3.4 3.0 4.2 2.0 3.6 3.2 3.4 7.7 3.7 4.4 Avg: 3.8, Min: 2.0, Max: 7.7, Diff: 5.7]
外部根扫描 - 扫描外部根(例如,指向堆的系统字典等)所花费的时间。
Update Remembered Set 更新记忆集
[Update RS (ms): 0.1 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 Avg: 0.0, Min: 0.0, Max: 0.1, Diff: 0.1] [Processed Buffers : 26 0 0 0 0 0 0 0 0 0 0 0 Sum: 26, Avg: 2, Min: 0, Max: 26, Diff: 26]
更新记忆集 - 在暂停开始之前,任何已完成但尚未被并发优化线程处理的缓冲区都需要更新。时间取决于卡片的密度。卡片越多,所需时间越长。
Scanning Remembered Sets 扫描记忆集
[Scan RS (ms): 0.4 0.2 0.1 0.3 0.0 0.0 0.1 0.2 0.0 0.1 0.0 0.0 Avg: 0.1, Min: 0.0, Max: 0.4, Diff: 0.3]F
扫描记忆集 - 查找指向收集集的指针。
Object Copy 对象复制
[Object Copy (ms): 16.7 16.7 16.7 16.9 16.0 18.1 16.5 16.8 16.7 12.3 16.4 15.7 Avg: 16.3, Min: 12.3, Max: 18.1, Diff: 5.8]
对象复制——每个线程用于复制和转移对象的时间。
Termination Time 终止时间
[Termination (ms): 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 Avg: 0.0, Min: 0.0, Max: 0.0, Diff: 0.0] [Termination Attempts : 1 1 1 1 1 1 1 1 1 1 1 1 Sum: 12, Avg: 1, Min: 1, Max: 1, Diff: 0]
终止时间——当一个工作线程完成其特定的对象复制和扫描任务后,它进入终止协议。它会寻找可以窃取的工作,一旦完成这些工作,它再次进入终止协议。终止尝试计数所有窃取工作的尝试。
GC Worker End GC 工作线程结束
[GC Worker End (ms): 7116.4 7116.3 7116.4 7116.3 7116.4 7116.3 7116.4 7116.4 7116.4 7116.4 7116.3 7116.3 Avg: 7116.4, Min: 7116.3, Max: 7116.4, Diff: 0.1] [GC Worker (ms): 20.4 20.3 20.3 20.2 20.3 20.2 20.2 20.2 20.3 20.2 20.1 20.1 Avg: 20.2, Min: 20.1, Max: 20.4, Diff: 0.3]
GC 工作线程结束时间——单个 GC 工作线程停止的时间戳。
GC 工作线程时间——单个 GC 工作线程所花费的时间。
GC Worker Other GC 工作线程其他
[GC Worker Other (ms): 2.6 2.6 2.7 2.7 2.7 2.7 2.7 2.8 2.8 2.8 2.8 2.8 Avg: 2.7, Min: 2.6, Max: 2.8, Diff: 0.2]
GC 工作线程其他 - 无法归因于前面列出的工作线程阶段的时间(对于每个 GC 线程)。应该相当低。在过去,我们曾看到过过高的值,并将其归因于 JVM 其他部分的瓶颈(例如,随着分层编译,代码缓存占用率的增加)。
Clear CT 清除 CT
[Clear CT: 0.6 ms]
清除 RSet 扫描元数据的卡表所花费的时间
Other 其他
[Other: 6.8 ms]
GC 暂停期间各种其他顺序阶段所花费的时间。
CSet
[Choose CSet: 0.1 ms]
确定要收集的区域集合所花费的时间。通常非常短;在需要选择旧区域时稍长。
Ref Proc
[Ref Proc: 4.4 ms]
处理从 GC 的前几个阶段推迟的软引用、弱引用等所花费的时间。
Ref Enq 引用入队
[Ref Enq: 0.1 ms]
将软引用、弱引用等放入待处理列表所花费的时间。
Free CSet 释放 CSet
[Free CSet: 2.0 ms]
花费时间释放刚刚收集的区域集,包括它们的记忆集。
# Summary 总结
在这个 OBE 中,您已经了解了 Java JVM 中包含的 G1 垃圾收集器的概述。首先,您学习了堆和垃圾收集器是任何 Java JVM 的关键部分。接下来,您回顾了如何使用 CMS 收集器和 G1 收集器进行垃圾收集。然后,您了解了 G1 命令行开关及其使用的最佳实践。最后,您学习了 GC 日志中包含的对象和数据的记录。
在本教程中,您已经学习了:
- Java JVM的组件
- G1收集器概述
- CMS收集器回顾
- G1收集器回顾
- 命令行开关和最佳实践
- 使用G1进行日志记录
# Resources 资源
- 欲了解更多信息和相关内容,请参阅这些网站和链接。
- Java HotSpot VM Options Java HotSpot VM 选项 (opens new window)
- The Garbage First(G1) Garbage Collector 垃圾优先(G1)垃圾收集器 (opens new window)
- Poonam Bajaj G1 GC Blog Post Poonam Bajaj G1 GC 博客文章 (opens new window)
- Java SE 7: Develop Rich Client Applications Java SE 7:开发富客户端应用程序 (opens new window)
- Java Performance - Charlie Hunt and Binu John Java 性能 - Charlie Hunt 和 Binu John (opens new window)
- Oracle Learning Library Oracle 学习库 (opens new window)