G1垃圾收集器入门-原创译文

时间:2023-11-23 14:20:02

G1垃圾收集器入门-原创译文

原文地址

Getting Started with the G1 Garbage Collector

概览

目的

本文介绍了如何使用G1垃圾收集器以及如何与Hotspot JVM一起使用的基础知识。您将了解G1收集器在内部的功能,使用G1的关键配置,以及G1收集器的操作日志选项。

完成所需时间

接近1小时

简介

本文涵盖了Java虚拟机(JVM)G1垃圾收集(GC)的基础知识。在本文的第一部分中,提供了JVM的概述以及垃圾收集和性能的介绍。接下来的您将回顾有关CMS收集如何在HotspotJVM工作原理。然后,一步一步地介绍在HotspotJVM上G1垃圾收集的工作原理。接下来,有一个章节介绍G1垃圾回收器可用的垃圾收集命令行选项。最后介绍G1收集器的日志。

硬件和软件环境

以下是硬件和软件要求的列表:

  • PC能运行Windows XP或更高版本,Mac OS X或Linux。注意,已经完成了Windows7开发,并没有在所有平台上进行测试。但是,一切都应该在OS X或Linux上正常工作。另外有多处理器的机器更好;
  • Java 7 Update 9或更高版本
  • 最新的Java&7 Demo和示例Zip文件。解压缩文件并放在一个目录中,例如: C:\javademos

前提条件

在开始本教程之前,您应该:

  • 如果没有下载JDK,请下载并安装最新版本的Java JDK(JDK 7 u9或更高版本)
  • 下载Demo和示例Zip文件。

Java技术和JVM

Java概览

Java是Sun公司于1995年首次发布的编程语言和计算平台。它是为Java程序(包括实用程序,游戏和商业应用程序)提供支持的基础技术。 Java遍布全球超过8.5亿台个人电脑,全球数十亿台设备,包括手机和电视设备。 Java由许多关键组件组成,作为一个整体创建了Java平台。

Java运行时版本

当您下载Java时,您将获得Java运行时环境(JRE)。JRE由Java虚拟机(JVM),Java平台核心类和支持Java平台库组成。所有这三个都需要在您的计算机上运行Java应用程序。使用Java7,Java应用程序可以作为桌面应用程序从操作系统运行,也可以作为桌面应用程序运行使用Java Web Start从Web安装,或者作为浏览器中的Web Embedded应用程序(使用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应用程序的预包装库的集合。JavaAPI提供了工具来完成许多常见的编程任务使开发变得更简单,包括字符串操作,日期/时间处理,网络和实现数据结构(例如列表,映射,堆栈和队列)。

JVM

Java虚拟机(JVM)是​​一种抽象计算机。 JVM是一个程序,看起来像写入要执行的程序的机器。这样,Java程序被写入同一组接口和库。每个操作系统都有特定的JVM实现,将Java程转换成本地操作系统上运行的指令和命令。这样Java程序就实现了平*立性。

第一个Java虚拟机的原型是Sun公司实现的,模拟了类似于当代个人数字助理(PDA)的手持设备托管的软件中的Java虚拟机指令集。 Oracle当前实现了移动端,桌面和服务器设备上的JVM。但Java虚拟机不承担任何特定的技术实现,主机硬件或主机操作系统。Java本质上不是解释型语言,但也可以通过将其指令集编译为CPU的指令集来实现。它也可以在微代码中或直接在CPU中实现。

JVM不关心Java变成语言,只有特定的二进制格式,类文件格式。类文件包含Java虚拟机指令(或字节码)和符号表以及其他辅助信息。但是,任何具有有效类文件表达功能的语言都可以有JVM托管。受到机器平台无关的吸引,其他语言的实现者可以将JVM转变为其语言的传送工具。

探索JVM架构

Hotspot架构

HotSpot JVM具有支持强大的功能的架构基础,并支持实现高性能和大规模可扩展性的能力。例如,HotSpot JVM JIT编译器能生成动态优化代码。换句话说,它们在Java应用程序运行时进行优化决策,并生成针对底层系统架构的高性能本地机器指令。此外,通过发展演进和持续的工程化,JVM的运行时环境和多线程垃圾收集器,即使是在最大的可用计算机系统,HotSpot JVM也可提供高可扩展性。

G1垃圾收集器入门-原创译文

JVM的主要组件包括类加载器,运行时数据区和执行引擎。

Hotspot 关键部分

与性能相关的JVM的关键组件在下图中高亮显示

G1垃圾收集器入门-原创译文

在JVM调优性能时需要关注三个组件。堆是存储对象数据的地方,该区域由启动时选择的垃圾收集器进行管理。大多数调优选项涉及到大小调整堆,并根据情况选择最合适的垃圾回收器。JIT编译器也对性能有很大影响,但很少需要使用较新版本的JVM进行调优。

性能基础

通常在调优Java应用程序时, 关注两个主要目标:响应性或吞吐量。随着教程的进行,我们将回顾这些概念。

响应

响应是指应用程序或系统响应所请求的数据的速度。示例包括:

  • 桌面UI响应事件的速度有多快
  • 网站返回页面的速度有多快
  • 数据库查询返回的速度有多快

对于专注于响应性的应用程序,不能接受大的暂停时间。重点是在短时间内作出回应。

吞吐量

吞吐量关注在特定时间内最大限度地提高应用程序的工作量。如何测量吞吐量的示例包括:

  • 在给定时间内完成的事务次数
  • 批处理程序可以在一小时内完成的作业数
  • 可在一小时内完成的数据库查询数

对于专注于吞吐量的应用,高暂停时间是可以接受的。由于高吞吐量的应用在较长时间内集中于基准测试,因此不需要考虑快速的响应时间

G1垃圾收集器

G1垃圾收集器

G1垃圾收集器是一种服务器端垃圾收集器,针对大内存的多处理器机器。它以高概率满足垃圾收集(GC)暂停时间目标,同时实现高吞吐量。Oracle JDK 7u4及更高版本中完全支持G1垃圾回收器。 G1收集器专为以下类型应用而设计:

  • 可以与CMS收集器等应用程序线程同时运行
  • 在紧凑的内存下,没有引发长时间的GC暂停时间
  • 需要更可预测的GC暂停持续时间
  • 不想牺牲大量的吞吐量性能
  • 不需要更大的Java堆

G1计划作为并行标记扫描收集器(CMS)的长期替代品。将G1与CMS进行比较,G1是更好的解决方案。第一个区别是G1是压缩型收集器。G1的压缩功能,足以完全避免使用细粒度的空闲内存进行分配,而是依赖于regions。这大大简化了收集器,并且消除了大部分的潜在碎片问题。此外,G1比CMS收集器提供更可预测的垃圾回收暂停时间,并允许用户指定所需的目标暂停时间。

G1工作原理概述

常规的垃圾收集器(串行,并行,CMS)都将堆结构分为三个部分:年轻代,老年代和固定大小的永久代。

G1垃圾收集器入门-原创译文

所有JVM内存对象最终都在这三个部分中的一部分,而G1收集器采取不同的方法。

G1垃圾收集器入门-原创译文

堆被分成一组大小相等的区域,每个是连续范围的虚拟内存。某些Regions被分配给和常规收集器一样的角色(eden区,survivor区,老年代),但他们没有固定的大小。这提供了更大的内存使用灵活性。

在执行垃圾收集时,G1以类似于CMS收集器的方式运行。G1执行并发的全局标记阶段来确定整个堆中对象的活动性。标记阶段完成后,G1知道哪些区域大部分是空的。它首先收集这些区域,这通常产生大量的可用空间。这就是为什么这种方式叫做垃圾收集优先。顾名思义,G1将其收集和压缩活动集中在可能充满可回收对象的堆区域,也就是垃圾。G1使用暂停预测模型来满足用户定义的目标暂停时间,并根据指定的目标暂停时间选择要收集的区域数量。

被G1标识成熟的区域是通过转移的方式收集。G1将对象从堆的一个或多个区域复制到堆上的单个区域,并且在此过程中,同时压缩和释放内存。这种转移方法在并行运行在多处理器上,以减少暂停时间并提高吞吐量。因而,对于每一次垃圾收集,G1都不断地减少碎片,并且在用户定义的暂停时间内工作。这种方式超出了以前的两种方法(指CMS和ParallelOld)的能力。CMS垃圾收集器不执行内存压缩,ParallelOld垃圾收集器执行全堆压缩,这将导致很大的暂停时间。

一个值得注意的点,G1不是一个实时垃圾收集器。它尽可能的符合设定的目标暂停时间,但是不能绝对实现。根据以前收集的垃圾时间,G1估计可以在用户指定的目标时间内收集多少个区域。因此,收集器具有收集区域的成本的相当准确的模型,并且使用该模型来确定目标暂停时间内收集哪些区域和多少区域。

注意:G1同时有并发(与应用程序线程一起运行,例如细化,标记,清理)和并行(多线程,例如STW)的阶段。FullGC仍然是单线程的,但是如果您的应用程序正确调优,应避免使用Full GC。

G1 踪迹

如果从Parallel Old收集器或CMS收集器迁移到G1,则可能会看到更大的JVM进程大小。这主要与"accounting"数据结构相关,如Remembered Sets(RSets)和Collection Sets(CSets)。

  • Remembered Sets (RSets)跟踪对象引用到给定的区域。堆中每个区域有一个RSet。 RSet使GC能并行和独立地收集一个区域。 RSets的整体性能影响小于5%
  • Collection Sets (CSets)是GC中将要被收集的一组区域。GC中的所有存活数据在GC期间转移(复制/移动)。这些集合可能是eden区,survivor区或者老年代,CSets对JVM的大小影响不到1%。

G1的推荐用例

G1的第一个重要特点是为用户的应用程序的提供一个低GC延时和大内存GC的解决方案。这意味着堆大小6GB或更大,稳定和可预测的暂停时间将低于0.5秒。

如果应用程序使用CMS或ParallelOld垃圾回收器具有一个或多个以下特征,将有利于切换到G1:

  • Full GC持续时间太长或太频繁
  • 对象分配率或年轻代升级老年代很频繁
  • 不期望的很长的垃圾收集时间或压缩暂停(超过0.5至1秒)

注意:如果你正在使用CMS或ParallelOld收集器,并且你的应用程序没有遇到长时间的垃圾收集暂停,则保持与您的当前收集器是很好的。升级JDK并不必要更新收集器为G1。

回顾分代GC和CMS

CMS(也称为并发低暂停收集器)收集器是收集老年代垃圾的。它尝试通过与应用程序线程同时执行大部分垃圾收集工作来最小化由于垃圾回收引起的应用暂停时间。通常情况下,CMS不会复制或压缩存活的对象,完成一次垃圾收集不需要移动存活的对象。如果有内存碎片问题,需要分配一个较大的堆。

注意: CMS收集器在年轻代的收集与parallel收集器使用相同的算法

CMS收集阶段

CMS收集器在老年代上执行以下阶段:

Phase Description
(1)初始标记(STW) 老年代对象被标记为可达,包括来自年轻代引用的对象;停顿时间相比于年轻代收集停顿时间更短
(2)并发标记 遍历老年代的可达对象,这个过程和应用线程并发执行;从标记的对象开始扫描,并将根目录中的所有可达对象进行标记。mutator在并发阶段2,3和5期间执行,并且在这一阶段CMS生成中分配的任何对象(包括年轻代晋升的对象)都将立即标记为存活。
(3)重新标记 (STW) 并发标记阶段查找因为并发标记阶段应用线程产生的遗漏的对象(该阶段是暂停JVM的)
(4)并发清理 收集在标记阶段收集标识为不可达的对象,死亡的对象被加到空闲列表供以后分配用,这一阶段可能会发生死亡对象的合并 注意:存活对象不会被移动
(5)重置 清除数据结构,准备下一次并发收集

备注(译者注):GC是JVM的一部分,mutator应该是JVM非GC的部分,职责是包括分配内存,read(从内存中读取内容),write(将内容写入内存)

回顾垃圾收集步骤

接下来,我们一步一步回顾CMS收集器操作

1. CMS收集器堆堆结构

JVM堆被分成三个空间

G1垃圾收集器入门-原创译文

年轻代被分成eden和2个survivor区。老年代是一个连续的空间,对象在此收集。老年代会不进行压缩,除非有FullGC。

2. YongGC 是如何工作的

下图中绿色的是年轻代,蓝色的是老年代。这就是你的应用程序运行一阵子后,使用CMS收集器的外观,对象散落在老年代区域

G1垃圾收集器入门-原创译文

使用CMS,老年代对象被重新分配。他们没有被移动,内存空间没有被压缩,除非有FullGC。

3. 年轻代收集

存活对象从Eden区和一个survivor区拷贝到另一个survivor区,任何一个“年龄”达到阈值的对象会被晋升到老年代。

G1垃圾收集器入门-原创译文

4. YoungGC之后

在YoungGC之后,Eden区和一个survivor区会被清理。

G1垃圾收集器入门-原创译文

新晋升老年代的对象用深蓝色表示,绿色的对象是还未晋升老年代的年轻代对象。

5. 老年代垃圾收集 - CMS

有2个STW(Stop-The-World)事件发生:初始标记和重新标记。当老年代分配的对象达到一定的比例,CMS被触发

G1垃圾收集器入门-原创译文

(1)初始标记是一个短暂的暂停阶段,其中标记存活(可到)对象。(2)并发标记会在应用程序执行的同时查找存活的对象。最后在重新标记的阶段(3),发现在上一阶段(2)并发标记期间遗漏的对象。

6. 老年代垃圾收集 - 并发清理

在前一阶段没有标记的对象会被释放,这一阶段没有压缩。

G1垃圾收集器入门-原创译文

注意: 未标记的对象==死亡对象

7. 老年代垃圾收集 - 清理之后

在阶段(4)并发清理之后,你能看到很多内存被释放。同时你也注意到没有压缩整理动作

G1垃圾收集器入门-原创译文

最后,CMS收集器将跳到复位阶段(5),等待下一次达到GC的阈值

G1垃圾收集器入门

G1收集器采用不同的方法来分配堆。接下来的图将逐步回顾G1系统

1. G1堆结构

堆内存会被切分成为很多个固定大小区域

G1垃圾收集器入门-原创译文

区域大小由JVM在启动时设置。 JVM通常针对2000个区域,大小从1到32Mb

2. G1 堆分配

实际上,这些区域在逻辑上被映射为Eden,Survivor和老年代

G1垃圾收集器入门-原创译文

途中标记为不同颜色的区域对应不同的角色。存活的对象从一个区域转移(即复制或移动)到另一个区域。区域被设计为并行收集垃圾,可能会暂停所有应用线程。

如图所示,区域可以分配到Eden,survivor和老年代。此外,还有第四种类型,被称为巨型区域(Humongous Region)。Humongous区域是为了那些存储超过50%标准region大小的对象而设计的,他们被存储在一系列的连续区域内,最后Humongous区域将是堆未使用的区域。

注意:在撰写本文时,收集Humongous对象尚未被优化。因此,您应避免创建此大小的对象。

3. G1的年轻代

堆被分成大约2000个区域,最小为1Mb,最大为32Mb。蓝色区域表示存储老年代的对象,绿色区域表示存储年轻代对象。

G1垃圾收集器入门-原创译文

注意: 这些区域不像常规垃圾收集器需要连续

4. G1的YoungGC

存活的对象会被准转移(拷贝或移动)到1个或者多个survivor区域。如果到达了年龄阈值,一些对象会被晋级到老年代的区域。

G1垃圾收集器入门-原创译文

这一阶段是暂停应用的(STW)。Eden和Survivor大小被计算下一次youngGC。统计信息有助于计算区域的大小,像目标暂停时间会被考虑到。这种方法使得非常容易调整区域的大小,根据需要变得更大或者更小。

5. G1 YoungGC结束

存活的对象已被转移到survivor区域或老年代

G1垃圾收集器入门-原创译文

最近晋级的对象用深蓝色表示,survivor区域用绿色表示

关于G1年轻代的总结如下:

  • 堆是一块内存区域被分成了很多区域
  • 年轻代内存是由一系列不连续的区域组成,这使得很容易根据需要调整大小
  • 年轻代垃圾收集,或者Young GC是暂停应用的(STW),执行回收时所有应用线程会被暂停
  • YoungGC多线程并行收集的
  • 存活的对象被拷贝到新的survivor区域或者老年代

G1的老年代垃圾收集

像CMS收集器,G1收集器设计的目标也是一个低暂停时间的老年代收集器。

G1的收集阶段,并发标记循环阶段

G1收集器在老年代上收集分为以下阶段。注意,一些阶段也是年轻代收集的一部分

Phase Description
(1) 初始标记(STW) 这是一个STW的事件,使用G1,初始标记阶段会在YoungGC附带。标记survivor区域对象(根区域),它们可能引用到老年代对象
(2) 根区域扫描 扫描survivor区域对象引用了老年代对象。这一阶段和应用程序并发,必须在YoungGC之前完成
(3) 并发标记 在整个堆上查找活动对象,并发标记和应用程序并发进行。这一阶段可以被YoungGC打断
(4) 重新标记(STW) 完成堆上所有存活对象的标记,使用一种叫snapshot-at-the-beginning (SATB) 的算法,该算法比CMS重新标记的算法高效很多
(5) 清理(STW和并发) (1)执行存活对象和完全空闲区域的统计(STW) (2)清空Remembered Sets(STW) (3)重置空闲区域并将它们返回到空闲列表(并发)
(*) 拷贝(STW) 这个阶段STW操作,转移和复制存活的对象到未使用的区域。这可以在年轻代执行,日志显示为[GC pause (young)]。或者同时在年轻代和老年代的区域执行,日志显示为[GC Pause (mixed)]

注释(译者注):SATB算法是重新标记阶段对可能漏标的white对象进行快速标记的算法,详情请参考R大写的文章

G1 老年代收集入门

在定义的阶段中,我们来看看它们如何与G1收集器中的老年代进行交互。

6. 初始标记阶段

存活的对象的初始标记附带在YoungGC中。在日志中显示为GC pause (young)(inital-mark)

G1垃圾收集器入门-原创译文

7. 并发标记阶段

如果找到空区域(被标记为“X”),它们在重新标记阶段立刻被删除。此外,决定是否存活的统计信息会被计算。

G1垃圾收集器入门-原创译文

8. 重新标记

空区域会被删除和回收,所有区域的存活信息会被统计。

G1垃圾收集器入门-原创译文

9.复制/清理阶段

G1选择“活跃度最低”的区域,因为这些区域最快被收集。这些区域和YoungGC同时被收集。这在日志中显示为[GC pause (mixed)],所以年轻代和老年代同时被收集。

G1垃圾收集器入门-原创译文

10. 复制/清理阶段之后

这些已被收集并整理的区域被表示为深蓝色和深绿色。

G1垃圾收集器入门-原创译文

G1老年代收集总结

综上所述,关于G1老年代垃圾回收有几个要点

  • 并发标记阶段
    1. 存活信息在应用线程运行时并发被统计
    2. 这些存活信息用来确定哪些区域在转移暂停阶段将会被最好的回收
    3. G1没有像CMS一样的清理阶段
  • 重新标记阶段
    1. 使用Snapshot-at-the-Beginning (SATB)算法使得重新标记比CMS更快
    2. 完全空的区域将会被回收
  • 复制/清理阶段
    1. 年轻代和老年代同时被回收
    2. 老年代收集的区域是根据他们的存活信息选择的

G1命令行选项和最佳实践

在本节中,我们来看看G1的各种命令行选项。

基本命令行

要使G1收集器,需要配置-XX: +UseG1GC

以下是一个简单的命令行启动Java2Demo,包括JDK demos and 例子。

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将尽力实现。因此,目标暂停时间的有时不一定能实现。默认值是200ms

  • -XX:InitiatingHeapOccupancyPercent=45 启动并发GC循环堆占用比例。G1基于整个堆的占用比例来触发并发GC循环。这个参数不仅作用在一个代(年轻代和老年代都有效),默认值是45%

最佳实践

使用G1时,应遵循一些最佳实践

不要设置年轻代的大小

使用G1收集器的默认行为,通过-Xmn显示指定年轻代大小

  • G1将不再遵循垃圾收集的目标时间。所以本质上,设置年轻代大小禁用目标暂停时间
  • G1不再能够根据需要扩大和收缩年轻代的空间。由于尺寸是固定的,所以不能改变大小

RT指标

请考虑设定值能满足90%以上的目标,而不是使用平均响应时间(ART)作为一个指标设置XX:MaxGCPauseMillis=N。这意味着90%的请求用户将不会遇到高于目标的响应时间。请记住,暂停时间是一个目标,不能保证始终得到满足。

什么是转移失败?

对象晋级失败表示在GC期间JVM用完了堆区域,转移survivors区域和晋级对象失败。堆不能再扩展,因为它已经是最大的了。配置参数-XX:+PrintGCDetails,通过观察空间溢出在GC日志中显示。但是代价很高!

  • GC仍然继续运行,所以空间必须被释放
  • 未成功复制的对象必须终生存在
  • 对区域的RSets的任何更新,CSets必须重新生成
  • 所有这些步骤代价都很大

如何避免转移失败

为了避免转移失败,考虑以下的措施:

  • 增大堆大小

    • 增加-XX:G1ReservePercent=n,默认是10
    • 如果需要更多的“空间”,G1会通过尝试离开预留内存来创建一个虚假的上限[重新翻译]
  • 更早地启动G1回收的循环

  • 使用-XX:ConcGCThreads=n选项增加标记线程的数量

完整的G1 GC开关列表

这是G1 GC开关的完整列表。请记得使用上述最佳实践

Option and Default Value Description
-XX:+UseG1GC G1收集器开关
-XX:MaxGCPauseMillis=n 设置最大的目标暂停时间,这是一个软性目标,JVM蒋尽量去实现
-XX:InitiatingHeapOccupancyPercent=n 启动并发GC的Java堆占用阈值,用于触发GC周期,不仅仅是触发一个代(例如G1)。值为0表示“始终启动GC循环”,默认值是45
-XX:NewRatio=n 年轻代/老年代的比例,默认值是2
-XX:SurvivorRatio=n eden/survivor比例,默认值8
-XX:MaxTenuringThreshold=n 最大的老年代阈值,默认值15
-XX:ParallelGCThreads=n 设置在垃圾回收器的并行阶段使用的线程数,默认值根据JVM平台而不同
-XX:ConcGCThreads=n 并发垃圾收集器将使用的线程数,默认值根据JVM平台而不同
-XX:G1ReservePercent=n 设置作为空闲空间的预留内存百分比,以降低晋级失败的可能性,默认值是10%
-XX:G1HeapRegionSize=n G1把Java堆细分成均匀大小的区域,这个参数设置region的大小。此参数的默认值根据堆的大小设置,最小值是1Mb最大值是32Mb

G1的日志

最后一个主题我们需要解决的是使用日志信息来分析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) -XX:+PrintGCDetails 设置更详细的日志级别,该选项显示以下信息

  • 每个阶段显示平均,最小和最大时间
  • 根扫描,RSet更新(已处理缓冲区信息),RSet扫描,对象复制,终止(具有尝试次数)
  • 还显示“其他”时间,例如花费在选择CSet,参考处理,引用排队和释放CSet的时间
  • 显示Eden,Survivors和堆总占用比例

(3) -XX:+UnlockExperimentalVMOptions -XX:G1LogLevel=最详细的日志级别,像PrintGCDetails级别,但是还包括每个线程的信息。

[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]

Determining Time

几个开关决定了GC日志中显示的时间

(1) -XX:+PrintGCTimeStamps - 显示自JVM启动以来经过的时间

样例输出

1.729: [GC pause (young) 46M->35M(1332M), 0.0310029 secs]

样例输出

[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)-XX:+ PrintGCDateStamps 为每个条目添加时间前缀

2012-05-02T11:16:32.057+0200: [GC pause (young) 46M->35M(1332M), 0.0317225 secs]

理解G1日志

为了理解GC日志,本节将使用实际GC日志输出定义一些术语。以下示例显示了日志的输出,其中包含您将在其中找到的术语和值的解释。

注意: 更多的日志信息,想查看Poonam Bajaj's Blog post on G1 GC logs

G1 Logging Terms Index

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

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]

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 Avg: 3.8, Min: 2.0, Max: 7.7, Diff: 5.7]

External root scanning - 外根部扫描所花费的时间 (例如, 像系统字典那样的指向堆内的对象)

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]

Update Remembered Set - 已经完成收集但是还未被并发线程处理的,在暂停开始之前任何缓冲区需要被更新。耗时取决于卡表的密度,卡表越多,需要的时间越长。

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

Scanning Remembered Sets - 扫描指向CSet的指针

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]

Object copy – 每个线程用于复制和转移对象的时间

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 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 worker end time – 每个GC工作线程停止的时间戳

GC worker time – 每个GC工作线程的耗时

GC Worker Other

[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 worker other - 不属于之前列的阶段的GC耗时(每个GC线程),这些耗时应该很低。在过去,我们看到该值过于高,并被归因于JVM其他部分的瓶颈(例如:逐层增加代码缓存占用率)

Clear CT

[Clear CT: 0.6 ms]

清理RSet扫描元数据表的时间

Other

[Other: 6.8 ms]

GC暂停的各种其他顺序阶段的耗时

CSet

[Choose CSet: 0.1 ms]

完成一系列regions收集的耗时;通常非常小;当选择清理老年代region会稍长一点

Ref Proc

[Ref Proc: 4.4 ms]

处理GC之前的阶段的soft,weak等引用的时间

Ref Enq

[Ref Enq: 0.1 ms]

处理soft,weak引用等待列表的耗时

Free CSet

[Free CSet: 2.0 ms]

释放刚被收集的regions集合的耗时,包括记录他们的集合(Cset)

总结

本文中,您已经了解了G1垃圾回收器的概述。首先,你了解了堆和垃圾收集器是JVM的关键部分。然后,回顾了CMS和G1垃圾收集器的工作原理。然后,您学习了G1命令行以及使用它们的最佳实践。最后,您学习了GC日志中的对象和数据。

在本教程中,您已经了解到:

  • Java JVM的组成部分
  • G1收集器的概述
  • 回顾CMS收集器
  • 回顾G1收集器
  • 命令行开关和最佳实践
  • G1的日志

资源

G1的相关和更多的信息,请参阅以下这些网站和链接: