目前关于jvm的垃圾回收器G1所有资料都是来自oracle的文档介绍,感兴趣的可以直接阅读:
https://www.oracle.com/technetwork/tutorials/tutorials-1876574.html。
G1 堆分配
实际上,这些区域被映射成 Eden、Survivor 和老年代空间的逻辑表示。
图中的颜色表示哪个区域与哪个角色相关联。活动对象从一个区域转移(即复制或移动)到另一个区域。区域被设计为在停止或不停止所有其他应用程序线程的情况下并行收集。
如图所示,区域可以被分配为 Eden、survivor 和 old generation 区域。此外,还有第四种类型的物体,称为巨大区域。这些区域旨在容纳大小为标准区域 50% 或更大的对象。它们存储为一组连续的区域。最后一种区域将是堆的未使用区域。
Java概述
Java 是一种编程语言和计算平台,由 Sun Microsystems 于 1995 年首次发布。它是支持 Java 程序(包括实用程序、游戏和商业应用程序)的基础技术。Java 运行在全球超过 8.5 亿台个人计算机上,以及全球数十亿台设备上,包括移动和电视设备。Java 由许多关键组件组成,这些组件作为一个整体构成了 Java 平台。
Java 运行时版
当您下载 Java 时,您将获得 Java 运行时环境 (JRE)。JRE 由 Java 虚拟机 (JVM)、Java 平台核心类和支持的 Java 平台库组成。要在您的计算机上运行 Java 应用程序,这三者都是必需的。在 Java 7 中,Java 应用程序可以作为操作系统中的桌面应用程序运行,也可以作为桌面应用程序运行但使用 Java Web Start 从 Web 安装,或者作为浏览器中的 Web 嵌入式应用程序(使用 JavaFX)运行。
Java编程语言
Java 是一种面向对象的编程语言,包括以下特性。
- 平台独立性 - Java 应用程序被编译成字节码,字节码存储在类文件中并加载到 JVM 中。由于应用程序在 JVM 中运行,因此它们可以在许多不同的操作系统和设备上运行。
- 面向对象 - Java 是一种面向对象的语言,它采用了 C 和 C++ 的许多特性并对其进行了改进。
- 自动垃圾收集 - Java 自动分配和释放内存,因此程序不会负担该任务。
- 丰富的标准库 - Java 包含大量预制对象,可用于执行输入/输出、网络和日期操作等任务。
Java 开发工具包
Java 开发工具包 (JDK) 是一组用于开发 Java 应用程序的工具。使用 JDK,您可以编译用 Java 编程语言编写的程序并在 JVM 中运行它们。此外,JDK 提供了用于打包和分发应用程序的工具。
JDK 和 JRE 共享 Java 应用程序编程接口 ( Java API )。Java API 是开发人员用来创建 Java 应用程序的预打包库的集合。Java API 通过提供工具来完成许多常见的编程任务,包括字符串操作、日期/时间处理、网络和实现数据结构(例如,列表、映射、堆栈和队列),从而使开发更容易。
Java虚拟机
Java 虚拟机 (JVM) 是一种抽象的计算机器。JVM 是一个程序,对于编写在其中执行的程序来说,它看起来像一台机器。通过这种方式,Java 程序被写入相同的接口和库集。特定操作系统的每个 JVM 实现将 Java 编程指令转换为在本地操作系统上运行的指令和命令。这样,Java 程序就实现了平台独立性。
Java 虚拟机的第一个原型实现是在 Sun Microsystems, Inc. 完成的,它模拟了由类似于现代个人数字助理 (PDA) 的手持设备托管的软件中的 Java 虚拟机指令集。Oracle 当前的实现在移动、桌面和服务器设备上模拟 Java 虚拟机,但 Java 虚拟机不假定任何特定的实现技术、主机硬件或主机操作系统。它不是固有的解释,但也可以通过将其指令集编译为硅 CPU 的指令集来实现。它也可以在微代码中或直接在硅中实现。
Java 虚拟机对 Java 编程语言一无所知,只了解特定的二进制格式,即类文件格式。类文件包含 Java 虚拟机指令(或字节码)和符号表,以及其他辅助信息。
为了安全起见,Java 虚拟机对类文件中的代码施加了很强的语法和结构约束。但是,任何具有可以用有效类文件表示的功能的语言都可以由 Java 虚拟机托管。被普遍可用的、独立于机器的平台所吸引,其他语言的实现者可以将 Java 虚拟机作为他们语言的交付工具。(一)Java虚拟机
探索 JVM 架构
热点架构
HotSpot JVM 的架构支持强大的特性和能力基础,支持实现高性能和大规模可扩展性的能力。例如,HotSpot JVM JIT 编译器生成动态优化。换句话说,它们在 Java 应用程序运行时做出优化决策,并生成针对底层系统架构的高性能本地机器指令。此外,通过其运行时环境和多线程垃圾收集器的成熟进化和持续工程,HotSpot JVM 甚至在最大的可用计算机系统上也能产生高可扩展性。

JVM 的主要组件包括类加载器、运行时数据区和执行引擎。
关键热点组件
下图中突出显示了与性能相关的 JVM 关键组件。

JVM 的三个组件在调优性能时重点关注。该堆是你的对象数据的存储位置。该区域然后由启动时选择的垃圾收集器管理。大多数调整选项与调整堆大小和选择最适合您的情况的垃圾收集器有关。JIT 编译器对性能也有很大影响,但很少需要使用较新版本的 JVM 进行调整。
性能基础
通常,在调整 Java 应用程序时,重点是两个主要目标之一:响应能力或吞吐量。随着教程的进行,我们将回顾这些概念。
反应能力
响应性是指应用程序或系统对请求的数据做出响应的速度。例子包括:
- 桌面 UI 对事件的响应速度有多快
- 网站返回页面的速度
- 返回数据库查询的速度
对于注重响应的应用程序,大的暂停时间是不可接受的。重点是在短时间内做出反应。
吞吐量
吞吐量侧重于在特定时间段内最大化应用程序的工作量。如何测量吞吐量的示例包括:
- 在给定时间内完成的交易数量。
- 批处理程序在一小时内可以完成的作业数。
- 一个小时内可以完成的数据库查询数。
对于专注于吞吐量的应用程序,高暂停时间是可以接受的。由于高吞吐量应用程序在较长时间内专注于基准测试,因此不考虑快速响应时间。
G1 垃圾收集器
Garbage-First (G1) 收集器是一种服务器式垃圾收集器,针对具有大内存的多处理器机器。它以高概率满足垃圾收集 (GC) 暂停时间目标,同时实现高吞吐量。Oracle JDK 7 update 4 和更高版本完全支持 G1 垃圾收集器。G1 收集器专为以下应用而设计:
G1 计划作为并发标记清除收集器 (CMS) 的长期替代品。将 G1 与 CMS 进行比较,有一些差异使 G1 成为更好的解决方案。一个区别是 G1 是一个压缩收集器。G1 充分压缩以完全避免使用细粒度的空闲列表进行分配,而是依赖于区域。这大大简化了收集器的各个部分,并且在很大程度上消除了潜在的碎片问题。此外,与 CMS 收集器相比,G1 提供了更多可预测的垃圾收集暂停,并允许用户指定所需的暂停目标。
G1 运营概览
较老的垃圾收集器(串行、并行、CMS)都将堆结构化为三个部分:年轻代、老年代和固定内存大小的永久代。
所有内存对象都在这三个部分之一中结束。
G1 收集器采用不同的方法。
堆被划分为一组大小相等的堆区域,每个区域都是一个连续的虚拟内存范围。某些区域集被分配了与旧收集器相同的角色(伊甸园、幸存者、旧),但它们没有固定的大小。这为内存使用提供了更大的灵活性。
在执行垃圾收集时,G1 以类似于 CMS 收集器的方式运行。G1 执行并发全局标记阶段,以确定整个堆中对象的活跃度。标记阶段完成后,G1 知道哪些区域大部分是空的。它首先在这些区域收集,这通常会产生大量可用空间。这就是为什么这种垃圾收集方法被称为垃圾优先。顾名思义,G1 将其收集和压缩活动集中在堆中可能充满可回收对象的区域,即垃圾。G1 使用暂停预测模型来满足用户定义的暂停时间目标,并根据指定的暂停时间目标选择要收集的区域数量。
G1 确定的适合开垦的区域是使用疏散收集的垃圾。G1 将对象从堆的一个或多个区域复制到堆上的单个区域,并在此过程中压缩和释放内存。这种疏散在多处理器上并行执行,以减少暂停时间并提高吞吐量。因此,每次垃圾回收时,G1 都会在用户定义的暂停时间内持续减少碎片。这超出了前面两种方法的能力。CMS(并发标记扫描)垃圾收集器不进行压缩。ParallelOld 垃圾收集仅执行全堆压缩,这会导致相当长的暂停时间。
需要注意的是,G1 不是实时收集器。它以高概率满足设定的暂停时间目标,但不是绝对确定的。根据之前收集的数据,G1 会估计在用户指定的目标时间内可以收集多少个区域。因此,收集器具有收集区域的成本的合理准确模型,并且它使用该模型来确定在保持在暂停时间目标内的同时收集哪些区域以及收集多少区域。
注意: G1 具有并发(与应用程序线程一起运行,例如细化、标记、清理)和并行(多线程,例如停止世界)阶段。完全垃圾收集仍然是单线程的,但如果调整得当,您的应用程序应该避免完全 GC。
G1 足迹
如果您从 ParallelOldGC 或 CMS 收集器迁移到 G1,您可能会看到更大的 JVM 进程大小。这主要与“记帐”数据结构有关,例如记住集和集合集。
记忆集或 RSet 跟踪对象引用到给定区域。堆中的每个区域有一个 RSet。RSet 实现了区域的并行和独立收集。RSSet 的总体足迹影响小于 5%。
Collection Sets或 CSets 将在 GC 中收集的区域集。在 GC 期间,CSet 中的所有实时数据都会被清除(复制/移动)。区域集可以是伊甸园、幸存者和/或老年代。CSets 对 JVM 大小的影响不到 1%。
G1 的推荐用例
G1 的第一个重点是为运行需要大堆且 GC 延迟有限的应用程序的用户提供解决方案。这意味着大约 6GB 或更大的堆大小,以及低于 0.5 秒的稳定和可预测的暂停时间。
如果应用程序具有以下一个或多个特征,那么今天使用 CMS 或 ParallelOldGC 垃圾收集器运行的应用程序将有利于切换到 G1。
注意:如果您正在使用 CMS 或 ParallelOldGC,并且您的应用程序没有遇到长时间的垃圾收集暂停,则可以继续使用当前的收集器。使用最新的 JDK 不需要更改为 G1 收集器。
- Full GC 持续时间太长或太频繁。
- 对象分配率或提升率差异很大。
- 不需要的长时间垃圾收集或压缩暂停(超过 0.5 到 1 秒)
- 可以与 CMS 收集器等应用程序线程并发运行。
- 紧凑的可用空间,没有冗长的 GC 引起的暂停时间。
- 需要更多可预测的 GC 暂停持续时间。
- 不想牺牲很多吞吐量性能。
- 不需要更大的 Java 堆。
回顾分代 GC 和 CMS
Concurrent Mark Sweep (CMS) 收集器(也称为并发低暂停收集器)收集年老代。它尝试通过与应用程序线程并发执行大部分垃圾收集工作来最大程度地减少由于垃圾收集引起的暂停。通常并发低暂停收集器不会复制或压缩活动对象。垃圾收集是在不移动活动对象的情况下完成的。如果碎片成为问题,请分配更大的堆。
注意:年轻代的 CMS 收集器使用与并行收集器相同的算法。
CMS 收集阶段
CMS 收集器对堆的老年代执行以下阶段:
阶段 | 描述 |
(1) 初始标记 (停止世界事件) | 老年代的对象被“标记”为可达,包括那些可以从年轻代可达的对象。与次要收集暂停时间相比,暂停时间的持续时间通常较短。 |
(2) 并发标记 | 在 Java 应用程序线程执行时,并发地遍历可访问对象的年老代对象图。从标记的对象开始扫描并传递标记从根可到达的所有对象。mutator 在并发阶段 2、3 和 5 期间执行,并且在这些阶段期间在 CMS 生成中分配的任何对象(包括提升的对象)都会立即标记为活动的。 |
(3) 备注 (停止世界事件) | 在并发收集器完成对对象的跟踪后,查找由于 Java 应用程序线程对对象的更新而被并发标记阶段遗漏的对象。 |
(4) 并发扫描 | 收集在标记阶段标识为无法访问的对象。死对象的集合将该对象的空间添加到空闲列表以供以后分配。此时可能会发生死对象的合并。请注意,活动对象不会移动。 |
(5) 重置 | 通过清除数据结构为下一次并发收集做准备。 |
查看垃圾收集步骤
接下来,让我们一步一步回顾 CMS 收集器的操作。
CMS 收集器的堆结构
堆被分成三个空间。
年轻代被分成伊甸园和两个幸存者空间。老年代是一个连续的空间。对象收集就地完成。除非有完整的 GC,否则不会进行压缩。
Young GC 在 CMS 中的工作原理
年轻代是浅绿色,老年代是蓝色。如果您的应用程序已经运行了一段时间,这就是 CMS 的样子。对象分散在老年代区域周围。
使用 CMS,老年代对象就地被释放。它们不会四处移动。除非有完整的 GC,否则不会压缩空间。
年轻一代收藏
活动对象从伊甸园空间和幸存者空间复制到另一个幸存者空间。任何达到老化阈值的旧对象都会被提升到老年代。
年轻GC之后
在一次年轻 GC 之后,Eden 空间被清除,并且其中一个幸存者空间被清除。
新提升的对象在图表上以深蓝色显示。绿色对象是幸存的年轻代对象,还没有提升到老年代。
使用 CMS 的老年代集合
两个停止世界事件发生:初始标记和评论。当老年代达到一定的占用率时,CMS 就会启动。
(1) 初始标记是一个短暂的暂停阶段,其中标记了活动(可达)对象。(2) 并发标记在应用程序继续执行的同时查找活动对象。最后,在(3)remark 阶段,找到在前一阶段(2)并发标记中遗漏的对象。
老一代集合 - 并发扫描
上一阶段未标记的对象将就地解除分配。没有压缩。
注意:未标记的对象 == 死对象
老一代合集-扫后
在(4) Sweeping阶段之后,可以看到释放了大量内存。您还会注意到没有进行压缩。
最后,CMS 收集器将通过 (5) 重置阶段并等待下一次达到 GC 阈值。
G1 收集器采用不同的方法来分配堆。下面的图片逐步回顾了 G1 系统。
G1 堆结构
堆是一个内存区域,分为许多固定大小的区域。
区域大小由 JVM 在启动时选择。JVM 通常针对大约 2000 个大小从 1 到 32Mb 不等的区域。
G1 堆分配
实际上,这些区域被映射成 Eden、Survivor 和老年代空间的逻辑表示。
图中的颜色表示哪个区域与哪个角色相关联。活动对象从一个区域转移(即复制或移动)到另一个区域。区域被设计为在停止或不停止所有其他应用程序线程的情况下并行收集。
如图所示,区域可以被分配为 Eden、survivor 和 old generation 区域。此外,还有第四种类型的物体,称为巨大区域。这些区域旨在容纳大小为标准区域 50% 或更大的对象。它们存储为一组连续的区域。最后一种区域将是堆的未使用区域。
注意:在撰写本文时,收集大量对象尚未优化。因此,您应该避免创建这种大小的对象。
G1中的年轻一代
堆被分成大约 2000 个区域。最小大小为 1Mb,最大大小为 32Mb。蓝色区域保存老年代对象,绿色区域保存年轻代对象。
请注意,这些区域不需要像旧垃圾收集器那样连续。
G1 中的年轻 GC
活动对象被疏散(即,复制或移动)到一个或多个幸存区域。如果满足老化阈值,则将一些对象提升到老年代区域。
这是一个停止世界 (STW) 的暂停。为下一个年轻的 GC 计算 Eden 大小和幸存者大小。保留会计信息以帮助计算大小。诸如暂停时间目标之类的事情都被考虑在内。
这种方法可以很容易地调整区域的大小,根据需要使它们变大或变小。
使用 G1 结束 Young GC
活动对象已被疏散到幸存者区域或老年代区域。
最近升级的对象以深蓝色显示。绿色的幸存者区域。
综上所述,关于 G1 中的年轻代可以说如下:
- 堆是分割成区域的单个内存空间。
- 年轻代内存由一组不连续的区域组成。这使得在需要时可以轻松调整大小。
- 年轻代垃圾收集或年轻 GC 是停止世界事件。停止所有应用程序线程以进行操作。
- 年轻 GC 使用多个线程并行完成。
- 活动对象被复制到新的幸存者或老年代区域。
带 G1 的老一代合集
与 CMS 收集器一样,G1 收集器被设计为老年代对象的低暂停收集器。下表描述了老年代的 G1 收集阶段。
G1 收集阶段 - 并发标记周期阶段
G1 收集器在堆的老年代执行以下阶段。请注意,某些阶段是年轻代集合的一部分。
阶段 | 描述 |
(1) 初始标记 (停止世界事件) | 这是一个停止世界的事件。对于 G1,它搭载在普通的年轻 GC 上。标记可能引用老年代对象的幸存者区域(根区域)。 |
(2) 根域扫描 | 扫描幸存者区域以获取对老年代的引用。当应用程序继续运行时会发生这种情况。该阶段必须在 Young GC 发生之前完成。 |
(3) 并发标记 | 在整个堆中查找活动对象。这发生在应用程序运行时。这个阶段可以被年轻代垃圾收集中断。 |
(4) 备注 (停止世界事件) | 完成堆中存活对象的标记。使用称为开始快照 (SATB) 的算法,该算法比 CMS 收集器中使用的算法快得多。 |
(5)清理 (停止世界事件和并发) |
|
(*) 复制 (停止世界事件) | 这些是世界暂停以将活动对象疏散或复制到新的未使用区域的停止。这可以通过记录为[GC pause (young)] . 或者记录为[GC Pause (mixed)] . |
G1 老年代收集循序渐进
定义了阶段后,让我们看看它们如何与 G1 收集器中的老年代交互。
初始标记阶段
活动对象的初始标记搭载在年轻代垃圾收集上。在日志中,这被标记为GC pause (young)(inital-mark)
.
并发标记阶段
如果发现空区域(如“X”所示),则在备注阶段立即将其删除。此外,计算确定活跃度的“会计”信息。
备注阶段
删除并回收空区域。现在计算所有区域的区域活跃度。
复制/清理阶段
G1 选择“活跃度”最低的区域,那些可以最快收集的区域。然后这些区域在年轻 GC 的同时被收集。这在日志中表示为[GC pause (mixed)]
。所以年轻代和年老代同时收集。
复制/清理阶段之后
选定的区域已被收集并压缩为图中所示的深蓝色区域和深绿色区域。
老年代GC总结
总之,关于老年代的 G1 垃圾收集,我们可以提出几个关键点。
- 并发标记阶段
- 活跃度信息是在应用程序运行时并发计算的。
- 此活跃信息确定在疏散暂停期间最好回收哪些区域。
- 没有像 CMS 那样的清扫阶段。
- 备注阶段
- 使用 Snapshot-at-the-Beginning (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 收集器的默认行为显式设置年轻代的大小。
响应时间指标
与其使用平均响应时间 (ART) 作为衡量指标来设置XX:MaxGCPauseMillis=<N>
,不如考虑设置在 90% 或更多时间内满足目标的值。这意味着 90% 的用户发出请求不会遇到比目标更长的响应时间。请记住,暂停时间是一个目标,并不能保证总能达到。
什么是疏散失败?
当 JVM 在 GC 期间为幸存者和提升的对象耗尽堆区域时发生的提升失败。堆无法扩展,因为它已经达到最大值。使用-XX:+PrintGCDetails
by时,这会在 GC 日志中指示to-space overflow
。这很贵!
如何避免疏散失败
为避免疏散失败,请考虑以下选项。
G1 GC 开关的完整列表
这是 G1 GC 开关的完整列表。请记住使用上面概述的最佳实践。
选项和默认值 | 描述 |
---|---|
-XX:+使用G1GC | 使用垃圾优先 (G1) 收集器 |
-XX:MaxGCPauseMillis=n | 设置最大 GC 暂停时间的目标。这是一个软目标,JVM 将尽最大努力实现它。 |
-XX:InitiatingHeapOccupancyPercent=n | 启动并发 GC 周期的(整个)堆占用百分比。它由基于整个堆的占用率触发并发 GC 周期的 GC 使用,而不仅仅是其中一代(例如,G1)。值 0 表示“执行恒定的 GC 循环”。默认值为 45。 |
-XX:新比率=n | 新生代/老年代大小的比率。默认值为 2。 |
-XX:幸存者比率=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。 |
- 增加堆大小
- 增加
-XX:G1ReservePercent=n
,默认为 10。 - G1 通过尝试让保留内存空闲以创建一个虚假的上限,以防需要更多“空间”。
- 增加
- 提前开始打标周期
- 使用该
-XX:ConcGCThreads=n
选项增加标记线程的数量。 - GC 仍然必须继续,因此必须释放空间。
- 未成功复制的对象必须就地保留。
- 必须重新生成对 CSet 中区域的 RSet 的任何更新。
- 所有这些步骤都是昂贵的。
- G1 将不再遵守集合的暂停时间目标。所以本质上,设置年轻代大小会禁用暂停时间目标。
- G1 不再能够根据需要扩展和收缩年轻代空间。由于大小是固定的,因此不能对大小进行更改。
使用 G1 记录 GC
我们需要讨论的最后一个主题是使用日志信息来分析 G1 收集器的性能。本节提供了可用于收集数据和打印在日志中的信息的开关的快速概览。
设置日志详细信息
您可以将详细信息设置为三个不同的详细级别。
(1)-verbosegc
(相当于-XX:+PrintGC
)将日志的详细程度设置为fine。
样本输出
[GC 暂停 (G1 Humongous Allocation) (young) (initial-mark) 24M->21M(64M), 0.2349730 secs] [GC 暂停(G1 Evacuation Pause)(混合)66M->21M(236M),0.1625268 秒]
(2)-XX:+PrintGCDetails
将细节级别设置为更精细。选项显示以下信息:
样本输出
[Ext Root Scanning (ms): Avg: 1.7 Min: 0.0 Max: 3.7 Diff: 3.7] [伊甸园:818M(818M)->0B(714M) 幸存者:0B->104M 堆:836M(4096M)->409M(4096M)]
(3)-XX:+UnlockExperimentalVMOptions -XX:G1LogLevel=finest
将细节级别设置为最精细。更精细,但包括单个工作线程信息。
[Ext Root Scanning (ms): 2.1 2.4 2.0 0.0 平均值:1.6 最小值:0.0 最大值:2.4 差异:2.3] [更新 RS(毫秒):0.4 0.2 0.4 0.0 平均值:0.2 最小值:0.0 最大值:0.4 差异:0.4] [处理过的缓冲区:5 1 10 0 总和:16,平均:4,最小值:0,最大值:10,差异:10]
确定时间
几个开关决定了时间在 GC 日志中的显示方式。
(1)-XX:+PrintGCTimeStamps
- 显示自 JVM 启动以来经过的时间。
样本输出
1.729:[GC 暂停(年轻)46M->35M(1332M),0.0310029 秒]
(2)-XX:+PrintGCDateStamps
- 为每个条目添加时间前缀。
2012-05-02T11:16:32.057+0200:[GC 暂停(年轻)46M->35M(1332M),0.0317225 秒]
理解 G1 日志
为了理解日志,本节使用实际的 GC 日志输出定义了许多术语。以下示例显示日志的输出以及您将在其中找到的术语和值的解释。
注意:有关更多信息,请查看Poonam Bajaj 关于 G1 GC 日志的博客文章。
G1 记录术语索引
Clear CT
CSet
External Root Scanning
Free CSet
GC Worker End
GC Worker Other
Object Copy
Other
Parallel Time
Ref Eng
Ref Proc
Scanning Remembered Sets
Termination Time
Update Remembered Set
Worker Start
平行时间
414.557:[GC 暂停(年轻),0.03039600 秒] [并行时间:22.9 毫秒] [GC Worker 启动(毫秒):7096.0 7096.0 7096.1 7096.1 706.1 7096.1 7096.1 7096.1 7096.2 7096.2 7096.2 7096.2 平均值:7096.1,最小值:7096.0,最大值:7096.2,差异:0.2]
Parallel Time – 暂停的主要并行部分的总耗用时间
Worker Start – Worker 开始的时间戳
注意:日志按线程 id 排序,并且在每个条目上都是一致的
外部根扫描
[ 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 平均值:3.8,最小值:2.0,最大值:7.7,差异:5.7]
外部根扫描- 扫描外部根所花费的时间(例如,指向堆的系统字典之类的东西。)
更新记忆集
[更新 RS(毫秒):0.1 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 平均值:0.0,最小值:0.0,最大值:0.1,差异:0.1] [处理的缓冲区:26 0 0 0 0 0 0 0 0 0 0 0 总和:26,平均:2,最小值:0,最大值:26,差异:26]
更新记住的集合- 在暂停开始之前,任何已完成但尚未被并发细化线程处理的缓冲区都必须更新。时间取决于卡片的密度。卡越多,需要的时间越长。
扫描记忆集
[扫描 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
扫描记忆集- 查找指向集合集的指针。
对象复制
[对象复制(毫秒):16.7 16.7 16.7 16.9 16.0 18.1 16.5 16.8 16.7 12.3 16.4 15.7 平均:16.3,最小值:12.3,最大值:18.1,8]差异:
对象复制——每个单独的线程花费在复制和清除对象上的时间。
终止时间
[终止(毫秒):0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 平均值:0.0,最小值:0.0,最大值:0.0,差异:0.0] [终止尝试次数:1 1 1 1 1 1 1 1 1 1 1 1 总和:12,平均值:1,最小值:1,最大值:1,差异: 0]
终止时间- 当工作线程完成其要复制和扫描的特定对象集时,它进入终止协议。它寻找要窃取的工作,一旦完成该工作,它就会再次进入终止协议。终止尝试计算所有窃取工作的尝试。
GC Worker 端
[ 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 平均值:7116.4,最小值:7116.3,最大值:7116.4,差异: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 平均值:20.2,最小值:20.1,最大值:20.4,差异:0.3]
GC worker end time – 单个 GC worker 停止的时间戳。
GC 工作时间- 单个 GC 工作线程所花费的时间。
GC Worker 其他
[ GC Worker 其他(毫秒):2.6 2.6 2.7 2.7 2.7 2.7 2.7 2.8 2.8 2.8 2.8 2.8 平均值:2.7,最小值:2.6,最大值:2.8,差异:0.2]
GC worker other – 不能归因于前面列出的工作阶段的时间(对于每个 GC 线程)。应该很低。过去,我们看到过高的值,它们被归咎于 JVM 其他部分的瓶颈(例如,Tiered 的代码缓存占用率增加)。
清晰CT
[清除 CT : 0.6 ms]
其他
[其他:6.8 毫秒]
GC 暂停的各个其他连续阶段所花费的时间。
集
[选择CSet : 0.1 毫秒]
最终确定要收集的区域集所花费的时间。通常很小;必须选择旧的时稍长。
参考程序
[参考过程:4.4 毫秒]
处理从 GC 的先前阶段推迟的软引用、弱引用等所花费的时间。
参考查询
[参考查询:0.1 毫秒]
将软引用、弱引用等放在待处理列表上所花费的时间。
免费 CSet
[免费 CSet:2.0 毫秒]
释放刚刚收集的区域集合所花费的时间,包括它们记住的集合。
- 显示每个阶段的平均、最小和最大时间。
- 根扫描、RSet 更新(带有已处理的缓冲区信息)、RSet 扫描、对象复制、终止(带有尝试次数)。
- 还显示“其他”时间,例如选择 CSet、引用处理、引用入队和释放 CSet 所花费的时间。
- 显示 Eden、Survivors 和 Total Heap 占用率。
在本 OBE 中,您已经概述了 Java JVM 中包含的 G1 垃圾收集器。首先,您了解了堆和垃圾收集器如何成为任何 Java JVM 的关键部分。接下来您回顾了如何使用 CMS 收集器和 G1 收集器进行垃圾收集。接下来,您了解了 G1 命令行开关以及使用它们的最佳实践。最后,您了解了 GC 日志中包含的日志记录对象和数据。
在本教程中,您学习了:
资源
有关更多信息和相关信息,请参阅这些站点和链接。
学分
- 课程开发者:Michael J Williams
- 质量保证:克里希南贾尼奇塔
- Java HotSpot VM 选项
- 垃圾优先(G1)垃圾收集器
- Poonam Bajaj G1 GC 博客文章
- Java SE 7:开发富客户端应用程序
- Java 性能 - Charlie Hunt 和 Binu John
- Oracle 学习库
- Java JVM 的组件
- G1 收集器概述
- CMS 收集器回顾
- G1收集器回顾
- 命令行开关和最佳实践
- 使用 G1 记录