Getting Started with the G1 Garbage Collector(译)

原文链接:Getting Started with the G1 Garbage Collector

概述

目的

这篇教程包含了G1垃圾收集器使用和它如何与HotSpot JVM配合使用的基本知识。你将会学到G1收集器内部是如何运作的,切换到使用G1的命令和记录其操作的日志选项。

完成时间

大概一个小时(我操,一个小时我勉强可以看完,要是这样边翻译边看还是需要点时间的呀

简介

这个OBE(我不知道OBE是什么意思)包含了Java中Java虚拟机(JVM)G1垃圾收集(GC)的基本知识。在这个OBE的第一部分,在介绍垃圾收集的同时也介绍了一下JVM。接下来,将带你们一起回顾一下CMS收集器如何与Hotspot协同工作的。然后,就是一步一步的引导大家学习当G1垃圾收集器搭配Hotspot的时候,JVM中的垃圾收集器是如何工作的。在这之后,将会有一部分讲解G1垃圾收集器的垃圾收集的命令行选项。最后你将会学习使用G1收集器时的垃圾收集日志选项。

硬件和软件要求

下面列举了硬件和软件的要求:

  • 运行Windows XP及后来版本,Mac OS或者Linux的PC。注意目前只在Windows 7上亲自测试过,而不是在所以的平台上都测试过。但是,在OS X或者Linux上一切都运行良好。同时,如果你的机器是多核的就更好了。
  • Java 7 Update 9或者以后的版本
  • 最新的Java 7演示和样例Zip压缩文件

前提

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

如果你还没有安装JDK的话,请先下载并安装最新版本的Java JDK(JDK 7u9或者之后版本)。
Java 7 JDK 下载

以及从相同地方下载样例压缩文件。解压并将其放到目录总。比如:C:\javademos

Java概述(以下都是废话。。。)

来不想翻译的,算了,还是慢慢翻吧

Java是Sun Microsystems公司于1955年发布的一门变成语言以及计算平台。它是支撑包括由Java编写的utilities,游戏和商务应用等程序的底层技术。Java运行在世界范围内超过8亿5千万台PC上以及数十亿的设备中,包括移动设备和TV。Java由一系列关键组件组成,并且他们作为一个整体,构成了Java平台。

Java运行时版本

当你下载了Java之后,你就获得了Java运行时环境(JRE)。JRE包括Java虚拟机(JVM),Java平台核心类,和支撑Java平台的库。以上3个都是在你的电脑上运行Java程序必不可少的。在Java 7中,Java程序可以作为操作系统的桌面应用运行,用Java Web Start可以从Web上安装并作为桌面应用运行,或者在浏览器中(使用JavaFX)作为Web嵌入程序运行。

Java编程语言

Java是一门面向对象的变成语言,它有如下特性。

  • 平台无关性- Java应用被编译成字节码,这些字节码存储在类文件中并加载到JVM中。由于应用是在JVM中运行的,所以它们可以在不同的系统和设备中运行。
  • 面向对象 - Java是一门面向对象的语言,从C和C++中吸取了很多特性并加以改进。
  • 自动垃圾收集 - Java可以自动分配和回收内存,所以应用程序不用受内存分配和回收等困扰。
  • 丰富的标准库 - Java包括了很多预定义的对象,这些对象可以很方便的完成输入/输出,网络通信以及其他数据处理的任务。

Java开发套件

Java Development Kit(JDK)是一套用于开发Java应用的工具。有了JDK,你可以编译Java语言编写的程序,并且在JVM上运行它们。除此之外,JDK也提供了一套用于打包和发布你的应用的工具。JDK和JRE共享Java应用程序接口(Java API)。Java API是预打包的库的集合,开发者可以利用Java API来开发他们的应用程序。有了Java API来完成许多开发中共同的任务使得开发过程更方便,比如字符串处理,日期/时间的处理,网络通信和数据结构的实现(比如链表,映射,栈和队列等)。(tmd,翻译的好累。。。

Java虚拟机(Java Virtual Machine)

Java虚拟机是一种抽象的计算机器。JVM是一个程序,但是对于那些在它上面运行的程序而言,它像是一个机器。这样一来,Java程序都是用同一套接口和库来编写的。每个在特定平台上面实现的Java虚拟机,都会把Java编程指令翻译成运行在本地操作系统上的指令和命令。如此,Java程序能够获得平台无关性。

 

JVM第一个实现的原型是由Sun Microsystems公司完成的,在类似于Personal Digital Assistant(PDA)的手持设备上模拟运行Java虚拟机指令。 Oracle's current implementations emulate the Java virtual machine on mobile, desktop and server devices, but the Java virtual machine does not assume any particular implementation technology, host hardware, or host operating system. It is not inherently interpreted, but can just as well be implemented by compiling its instruction set to that of a silicon CPU. It may also be implemented in microcode or directly in silicon.

 

Java虚拟机其实并不知道什么是Java编程语言,它仅仅知道一种特殊的二进制格式,即类文件格式。一个类文件中包含了Java虚拟机指令(字节码)和符号表,以及其他的一些辅助信息。

 

为了安全起见,Java虚拟机对类文件中的代码施加了严格的句法和结构的限制。然而,任何能够表达成有效类文件的变成语言都可以在Java虚拟机中运行。由于平台无关性以及广泛可用性,其他语言的实现者也可以将起放到Java虚拟机中运行。(1) The Java Virtual Machine

探索JVM的架构

Hotspot架构

HotSpot JVM的架构可以有力的支持高性能和大规模的可扩展性的特征和能力。例如,HotSpot JVM JIT编译器能够生成动态优化的代码。换句话说,它们在Java程序运行的时候可以进行优化决策,并且对于底层系统架构来说可以生成高质量的本地机器码。除此之外,通过成熟的改进以及连续的运行环境工程化和多线程的垃圾收集器,HotSpot JVM甚至在最大型的计算机中都可以获得高可扩展性。

 

image

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

 

Hotspot中的关键组件

下图中描述了JVM中和性能密切相关的组件。

 

image

图2 JVM中和性能密切相关的组件

 

在进行性能调优的时候,JVM中有3个组件需要关注。Java堆是你的对象数据存储的地方。这块区域是由Java虚拟机开启时所选择的垃圾收集器来管理的。大多数的调优选项都是根据你的应用场景设置合适的堆尺寸以及选择最合适的垃圾收集器。JIT编译器对于性能也有很大的影响,但是对于新版本的JVM来说,这个已经很少需要调整了。

性能指标

一般来说,当对一个Java应用程序调优的时候,关注的焦点通常是响应或者吞吐量这二者之一。在本教程的后面会回过头来再看这两个概念。

响应速度

响应速度指的是一个应用或者系统对请求过来之后反应的快慢。典型的例子有:

桌面UI对事件的反应快慢

一个网站返回页面速度有多快

一个数据库查询返回数据有多快

对于讲求响应速度的应用来说,长时间的暂停是不可接受的。所以需要在短时间内就可以响应。

吞吐量

吞吐量关注的是使一个应用在特定的时间内完成最大的工作量。衡量吞吐量的例子有以下几种:

在给定时间内完成的事务。

一个小时内一个批处理程序可以完成的工作量。

在一个小时内一个数据库可以完成的查询数量。

对于吞吐量来说,可以忍受长时间的暂停。由于高吞吐量的任务关注的是以长时间的基准,快速响应时间并不在考量的范围之内。

G1垃圾收集器

G1垃圾收集器是一款适用于服务器的垃圾收集器,这些服务器一般有多个处理器以及很大的内存。它能够很好的满足high probability同时又能达到很高的吞吐量。Oracle JDK7 update 4以及后来版本都完全支持G1垃圾收集器。G1垃圾收集器专为以下应用设计的:

能够像CMS收集器一样和应用线程一起并发操作。

能够压缩可用空间,而不会出现因垃圾收集导致的漫长的暂停时间。

需要更多可预测的GC暂停时间。

不想牺牲很多的吞吐量性能。

不需要过多的Java堆。

 

G1计划作为Concurrent Mark-Sweep(CMS)垃圾收集的长期替代。和CMS相比较,G1有很多不同之处使得G1比CMS更好。其中的一个特点是,G1是一个带压缩的垃圾收集器。G1能够充分的压缩可用内存来避免出现使用细粒度的可用空间列表进行内存分配,而是依赖于区域。这极大地简化了垃圾收集的一部分工作,最大程度地减少了可能存在的碎片化问题。同时,G1比CMS提供了更多的可预测的垃圾收集暂停,并且允许用户指定想要的暂停目标。(感觉挺牛逼的啊

G1 Operational Overview(这个Operational怎么翻译。。。。)

 

以前的垃圾收集器(serial,parallel,CMS)都将堆分为以下3个部分:年轻代,老年代,和固定内存大小的永久代。

 

 

image

图3 以前的垃圾收集器对堆的划分

所有的内存中的对象最终都会落到这3个区域的其中一个。但是,G1收集器采用了一种不同的方式。

 

image

图4 G1垃圾收集器对堆的划分

 

堆被划分为同等大小的堆区域的集合,每一块在虚拟内存中都的地址都是相接的。(这句话的意思是在虚拟内存中你可以想象这些块都是放在一起的,但是在实际的物理内存中它们可能相隔n远)某些区域的集合被分配相同的角色(比如eden,survivor,old),就像在以前的垃圾收集器中那样,但是它们并没有固定的大小。这在内存的使用上提供了极大的弹性。

 

在进行垃圾回收的时候,G1的操作方式类似于CMS收集器。G1进行并发的全局标记阶段来决定整个堆中对象是否存活。在标记阶段完成之后,G1会知道哪些区域的大部分是空的。它会先收集这些区域,从而会有大量的空闲空间。这就是为什么这种垃圾收集方法叫做Garbage-First(同志们,这就是为啥它叫G1啊)。正如它的名字所暗示的那样,G1将它的收集和压缩活动集中在那些尽可能装满可回收对象的区域,即,垃圾。G1使用暂停预测模型来满足用足自定义的暂停时间目标,根据特定的暂停时间目标来选择准备回收区域的数量。(有点好奇它的暂停预测模式是怎么构建的?

 

被G1识别为到已经达到可回收时机的区域将会将会用撤走(离场?evacuation)的方法进行回收。G1会将堆中一个或者多个区域中的对象复制到堆中的一个单一的区域,在这个过程中会同时对内存进行压缩和释放。这种evacuation的动作在多处理器的CPU上是并发进行,这样可以减少暂停时间并提高吞吐量。如此,每一次垃圾回收时,G1都会在用户自定义的暂停时间中持续工作并减少内存的碎片化。这种能力超过了以往的任何方法。CMS(Concurrent Mark Sweep)垃圾收集器不会对内存进行压缩。ParallelOld垃圾收集器只会对整个堆进行压缩,因此导致了相当长时间的暂停。

 

必须要注意到G1并不是一个实时的垃圾收集器。它会以很高的可能性来满足设定的暂停时间目标,但这并不是一定的。根据之前的垃圾收集数据,G1会对用户自定的目标时间内可能进行回收的区域个数进行估算。因此,G1收集器对收集的区域会有一个合理准确的模型,然后它利用这种模型来决定在暂停时间内回收哪些以及有多少区域会被回收。

 

注意:G1既有并发(和应用线程一起执行,比如refinement,标记和清理)阶段也有并行阶段(多线程,比如stop-the-world)。Full GC仍然是单线程的,如果调优得当的话,你的应用应该可以避免full GC。

G1空间占用(Footprint)

如果你从ParallelOldGC或者CMS收集器转而使用G1,你很有可能发现JVM进行占用的空间更大了。这其中大部分和"审计"数据结构有关,比如Rembered Sets和Collection Sets。

Remembered Sets或者RSets记录那些到一个给定的区域中对象的引用。在堆上每个区域都有一个RSet。RSet使得一个区域的并行和独立收集变得可能。RSets对真个空间占用的影响小于5%。

Collection Sets又或者叫CSets是那些将会在下一次GC中被回收的区域集合。所有在CSet中存活的对象都会在下一次GC是被evacuated(copied/moved)。这些区域的集合能是Eden,survivor或者老年代。CSets对JVM占用内存的影响小于1%。

建议使用G1垃圾收集器的场景

G1第一个关注点是为用户运行的那些需要很大的堆内存又要短的GC延迟提供一种解决方案。这意味着堆的大小在6GB或者更大时,稳定的可预测的暂停时间将小于0.5秒。

对于目前使用CMS或者ParallelOldGC垃圾收集器的应用,如果具有以下一个或者超过一个特征的,换成G1收集器将会对性能有很大提升。

  • Full GC停留的时间太长或者太频繁。
  • 新对象分配的速率或者对象老化的速率随时间变化很大。
  • 比预期长得多的垃圾收集时间或者压缩暂停的时间(比0.5到1秒还久)。

注意:如果你正在使用CMS或者ParallelOldGC并且你的应用并没有受长时间的垃圾收集暂停时间之苦,那么最好还是保持使用你当前的垃圾收集器。对于使用最新的JDK而言,切换到G1并不是必须的。

回顾CMS中的垃圾收集

回顾一般的垃圾收集以及CMS

 

并发标记清除(Concurrent Mark Sweep)收集器(又称为concurrent low pause collector)收集老年代。它试图通过和应用线程并发工作时最大程度地完成垃圾收集任务来最小化暂停时间。通常CMS不会复制或者压缩存活的对象。这种垃圾收集方式不会移动存活的对象。如果碎片化导致问题了,那么采用的方法就是分配一个更大的堆。

注意:CMS收集器使用和并行收集器一样的算法来回收年轻代。

CMS垃圾收集的过程

 

CMS通过以下阶段来完成对堆中老年代的收集工作:

 

 

Phase

Description

(1)初始标记
(Stop the World事件)
在老年代的对象被标记为可达,其中包括那些从年轻代可达的对象。相对于minor GC的暂停时间,初始标记导致的暂停时间很短。
(2)并发标记在Java应用线程运行的同时遍历老年代中可达的对象。从已被标记的对象开始,并且从根节点依次遍历可达的对象。mutators从并发阶段2,3和5开始执行并且任何在CMS阶段分配的对象(包括被promotion的对象)都立即被标记为存活。
(3)重新标记
(Stop the World事件)
找到那些在并发标记阶段在并发标记收集器完成跟踪之后由于Java应用线程更新导致错过的对象。
(4)并发清除收集那些在标记阶段标记为不可达的对象。死亡对象的收集增加了可以用后面分配的空闲列表的空间。死亡对象的Coalescing可能发生在这个阶段。注意存活的对象并没有移走。
(5)重新设定通过清理数据结构准备下一次的并发收集。

 

回顾垃圾收集的步骤

接下来,让我们回顾一下CMS垃圾收集器是怎么一步一步进行垃圾收集的。

Next, let's review CMS Collector operations step by step.

1. CMS垃圾收集器中的堆结构

堆被分成了3个空间。

 

image

图5 CMS中的堆划分

 

年轻代被分成了1个Eden空间和2个survivor空间。老年代连续的空间(contiguous)。

2. CMS中的年轻代GC是如何进行的?

在下图中年轻代用绿色标注了,而老年代则是蓝色的。如果你的程序运行了一会儿的话,你的Java堆可能就长成现在这样。对象分散在老年代中。

 

image

图6 CMS中对象的分布

在CMS中,老年代中的对象都是原位回收的。它们不会被到处移动。只有在执行Full GC的时候,老年代的空间才会被压缩。

 

3. 年轻代的收集

 

存活的对象都从Eden空间和Survivor空间复制到另一个Survivior空间中。任何到达了年龄阈值的对象都会提升到老年代中。

image

图7 年轻代的收集

 

4. 年轻代收集之后

 

image

图8 年轻代收集之后

 

新提升到老年代的中的对象在图8中都用深蓝色标注。绿色的对象都是未被提升到老年代的年轻代中存活下来的对象。

 

5. CMS中老年代的收集

两个stop-the-world的事件会发生:初始标记和重新标记。当老年代到达了一定的占用率之后,CMS收集便拉开了序幕(kicked off)。

 

image

图9 CMS中老年代的收集

 

(1)初始标记中存活的(可到达的)对象会被标记,这是一个短暂停的过程。

(2)并发标记在应用线程执行的同时找到存活的线程。

最后在(3)重新标记阶段,会找到之前(2)中并发标记中被错过的对象。

 

6. 老年代的收集 - 并发清除

 

在之前阶段中没有被标记的对象都会被回收。但并不会对标记的对象压缩。

 

image

图10 老年代的收集 - 并发清除

 

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

 

7. 老年代的收集 - 清除之后

 

在(4)清除阶段之后,你可以发现,很多的内存都被释放掉了。同时也会发现没有进行压缩过程。

 

image

图11 老年代的收集 - 清除之后

 

最后,CMS收集器会到(5)阶段,即重置阶段并等待下一次GC的到来。(GC。。。我不纯洁了)

 

G1垃圾收集器详细介绍

G1垃圾收集器详细介绍

G1垃圾收集器采用不同的方式来对堆进行分配。下面的图片一步一步回顾了G1系统。

1. G1堆结构

堆内存被划分为相同大小的固定区域。

 

image

 

在启动的时候JVM会选择区域的大小。JVM通常会将堆分为2000个区域,每个区域大小大概为1到32Mb之间。

 

G1堆的划分

实际上,这些区域被映射到逻辑上的Eden,Survivor和Old Generation Space。

 

image

 

图中不同的颜色代表了每块区域对应的角色。存活的对象被evacuated从一个区域到另一区域。区域的回收被设计为并行模式,既可以和应用线程共存,也可以和stop-the-world。正如锁演示的那样,这些区域可以被分配到Eden,survivor和old generation。除此之外,还有被被称之为Humongous区域的第4种类型的区域。这些区域使用来容纳那些占标准区域大小50%甚至以上的对象的。它们存储时作为相邻的区域。最终最后这种类型的区域是堆中未使用的区域。

注意:在这篇教程写的时候,对humongous对象的回收并没有进行优化。因此,你需要避免创建这类型的对象。

 

G1中的年轻代

整个堆大约被分为2000个区域。最小的区域是1Mb,最大的区域是32Mb。蓝色的区域持有老年代对象而绿色的区域则是持有年轻代对象。

 

image

 

注意,这些区域并不像以前的垃圾收集器中那样需要相邻。

G1中的一次年轻代收集

存活的对象会别evacuated到一个或则更多的survivor区域中。如果达到了老化阈值,某些对象会被提升到老年代的区域中。

 

image

 

这是一次Stop-The-World的暂停。Eden的大小和Survivor的大小会被计算出来为下一次年轻代的GC做准备。会保持审计信息来帮忙计算大小。同时也考虑到暂停时间之类的目标。这种方式使得非常容易地重新调整区域的大小,只要需要,可以随时的扩大或者缩小。

G1中年轻代收集结束时

存活的对象被evacuated到survivor区域或者老年代中。

 

image

 

最近提升的对象用深蓝色表示。Survivor区域则用绿色表示。

用一下的话来总结一下G1中年轻代的收集:

  • Java堆是被分为很多区域的单块内存空间。
  • 年轻代的内存由一系列不连续的空间组成。
  • 年轻代的收集是会导致Stop-The-World的。所有的应用线程在这个过程中会停止。
  • 年轻代的收集是多线程并行进行的。
  • 存活的对象都被复制到新的survivor或者老年代区域中。

Old Generation Collection with G1

就像CMS收集器一样,G1收集器被设计为对于老年代对象的收集低暂停时间。小表中描述了G1收集器中老年代的G1收集阶段。

 

G1收集阶段 - 并发标记Cycle阶段

G1收集器会对堆中的老年代执行以下阶段。要注意其中某些阶段是在年轻代收集中的。

 

阶段

描述

(1)初始标记(Stop-The-World)这是一个会Stop-The-World的事件。在G1中,这个阶段是借助(piggyback)于一次正常的年轻代GC的。标记survivor(root regions)中有引用到老年代中的对象。
(2)Root Region扫描扫描survivor区域中那些会进入老年代的引用。当程序恢复运行的时候会进行。这个阶段必须要在年轻代垃圾收集之前完成。
(3)并发标记找到整个堆上存活的对象。这个在应用运行的时候发生。这个阶段可以被年轻代的垃圾收集打断。
(4)重新标记阶段(Stop-The-Word)完成堆上存活对象的标记。使用被称之为snapshot-at-the-beginning(SATB)的算法,这要比CMS收集器中的算法快得多。
(5)清理(Stop-The-World和并发)
  • 执行对存活对象的审计并完成空闲区域的释放。(Stop-The-World)
  • Scrubs Remembered Sets。(Stop-The-World)
  • 重置空闲的区域并将它们返回到空闲列表。(Concurrent)
(*)复制(Stop-The-World)将存活的对象evacuate或者copy到新的未使用的区域中都是Stop-The-World的暂停。这些和年轻代区域时候完成,在日志中输出为[GC pause (young)]。或者在年轻代区域和老年代区域中同时发生,日志为[GC Pause (mixed)]。

 

定义了以上的阶段之后,我们来看一下这些阶段如何与G1收集器中的老年代交互的。

 

6. 初始标记阶段

存活对象的初始标记是附着于年轻代的垃圾收集的。在日志中的输出是标为GC pause (young)(initial-mark)。

 

image

 

7. 并发标记阶段

如果有空的区域被找到(用"X"表示的),它们马上会在重新标记阶段被清除掉。同样,决定是否存活的审计信息也会被计算出来。

 

image

 

8. Remark Phase

空的区域会被移走和收回。而且所有区域的是否存活都计算出来了。

 

image

 

9. 复制/清理阶段

G1选择那些具有最低"存活性"的区域,这些区域能够以最快的速度收集。然后这些区域会在和年轻代GC相同的时间被回收。这日志中的输出是[GC pause (mixed)]。因此,年轻代和老年代的回收都是在同一时间。

 

image

 

10. 复制/清理之后的阶段

选择的区域被回收并被压缩到深蓝色的区域中,深蓝色的区域在下图中显示了。

 

image

 

老年代中GC的总结

 

这里有些关键的地方要对老年代进行总结一下:

  • 并发标记阶段:存活性的信息会在应用程序运行的时候同时进行。存活信息表明了那些在evacuation阶段最适宜被收回的区域。这里没有像CMS中那样的Sweeping阶段。
  • 重新标记阶段:使用Snapshot-at-the-Beginning算法,要比CMS中的算法更快。完全为空的区域会被回收。
  • 复制/清理阶段:年轻代和老年代会在同一时刻被回收。老年代区域的选择是基于它们的存活性的。

命令行选项和最佳实践

 

这一部分我们来看一下G1中的命令行选项。

基础的命令行

为了激活G1垃圾收集器,可以使用: -XX:+UseG1GC

这里有一个启动Java2Demo,这个例子包含在JDK的demos中

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:InitatingHeapOccupancyPercent=45 当堆的空间占用达到这么多之后会启动一个并发的垃圾收集周期。G1中正是使用的这种整个对的占用,而不是某个代中的占用。如果设置为0就表示"进行constant垃圾收集周期"。这个选项的默认值是45(也就是对的占用达到了45%)。

 

最佳实践

这里提供了一些你在使用G1垃圾收集器时应该遵循的一些最佳实践。

 

不要设置年轻代的大小

 

通过-Xmn来显式地设置年轻代的大小会干预(meddles)到CG1收集器的默认行为。

  • G1将不会再遵循收集时的暂停时间目标。所以大体上(in essence),设置了年轻代的大小会禁用掉暂停时间目标。
  • G1将不能随着需要而扩大或者收缩年轻代空间的大小。由于大小被固定了,对于其大小不会由任何改变。

 

响应时间度量

设置XX:MaxGCPauseMillis=<N>,而不是使用平均响应时间(ART),是的满足目标的概率达到90%或者更多。这意味着90%的用户发出请求时将不会超过预设的时间目标。记住,暂停时间是一个目标,而不是必须满足的一个条件。

 

Evacuation Failure会怎么样?

 

为了survivors或者提升年轻代而进行GC是,会因为JVM用光了java堆而导致promotion failure。堆由于已经达到了最大的尺寸而不能继续扩展。这在用-XX:+PrintGCDetails时会to-space overflow而显示。这将导致花费增加。

 

GC会继续进行使得空间必须被释放。

复制失败的对象将必须提升到老年代中。

任何在CSets中的RSets的更新将重新生成。

所有以上的步骤的花费都非常大。

 

如果避免Evacuation Failure

 

为了避免Evacuation Failure,考虑一下选项。

增加堆的大小:增加-XX:G1ReservePercent=n,默认的值是10。G1会创建一个false ceiling通过试图使reserve memory空间以避免需要更多的"to-space"。

更早地开始标记周期。

使用-XX:ConcGCThreads=n选项来增加标记线程。

 

完整的G1垃圾收集的选项

 

一下是完整的G1收集选项,记得使用上面的最佳实践哦。

选项及其默认值描述
-XX:+UseG1GC使用G1垃圾收集器
-XX:MaxGCPauseMillis=n设置最大的暂停时间目标。这是一个软性的目标,并且JVM会努力达到这个目标。
-XX:InitiatingHeapOccupancyPercent=n启动并发GC的堆内存使用的百分比。这个选项被GC用来根据整个Heap占用百分比来触发一次并发的垃圾收集周期,而不是因为某一代的占用率。如果这个值为0,表示进行"do constant GC周期"。默认值是45。
-XX:NewRatio=n新/老代的大小之比,默认是2。
-XX:SurvivorRatio=neden/survivor空间的比,默认值是8。
-XX:MaxTenuringThreshold=n最大的老化阈值,这个默认值是15。
-XX:ParallelGCThreads=n设置垃圾收集并行阶段的线程数量。默认的值随着JVM运行的平台不同而不同的。
-XX:ConcGCThread=n垃圾收集在并行阶段所使用的线程数。默认的值随着JVM运行的平台不同而不同的。
-XX:G1ReservePercent=n为了减少promotion失败可能性而设置的heap的保留的false ceiling。默认值是10。
-XX:G1HeapRegionSize=n在G1中,Java堆被分为同一大小的区域。这个选项设定了区域的大小。这个值是根据所分配的堆的大小不同而不同的。最小值为1Mb,最大值为32Mb。

 

G1中的GC日志

我们要讲的最后一个主题是用来分析G1垃圾收集器的日志信息。这个部分提供你能使用收集信息和垃圾收集日志中打印的信息的选项。

 

设置日志细节

你可以设置3中不同垃圾收集的日志级别。

(1)-verbosegc(这个等同于-XX:+PrintGC),设置了级别为fine。

输出样例

[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设置了日志界别为finer。这个选项提供了一下信息:

  • 每个阶段的平均,最小和最大时间
  • Root Scan,RSet更新(已经处理的buffer信息),RSet Scan,对象复制,终结(尝试的次数)。
  • 同时也显示了其他时间比如选择CSet花费的时间,引用处理,引用入列和释放CSet。
  • 显示Eden,Survivor和整个堆的占用。

 

输出样例

 

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

 

(3)-XX:+UnlockExperimentalVMOptions –XX:G1LogLevel=finest 设置细节级别为finest。比finer还多出了每个工作线程的信息

 

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

Determing Time

有一些开关使用来调整GC日志中的时间如何显示的。

(1)-XX:+PrintGCTimeStamps 显示JVM启动之后的经过的时间。

样例输出

 

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

 

(2)-XX:+PrintGCDateStamps 每条日志前面增加了日期信息

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

 

理解GC日志

为了理解日志,这个部分定义了一些在GC输入的日志中所用的术语。一下输出显示了一些GC日志中扩展的术语。

 

注意:如果需要更多的信息,请访问Poonam Bajaj's Blog post on G1 GC logs

 

GC日志术语索引

 

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 – 垃圾线程开始的时间戳
Note: The logs are ordered on thread id and are consistent on each entry

注意:这个日志是按照线程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]
External root scanning - 用于扫描外部的根对象花的时间(比如,像执行堆的system dictionary)

 

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 - 已经完成的但是还没有被concurrent refinement线程处理的缓冲。Time depends on density of the cards. The more cards, the longer it will take.

 

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 - Look for pointers that point into the Collection Set.

 

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 – The time that each individual thread spent copying and evacuating objects.

 

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]
Termination time - When a worker thread is finished with its particular set of objects to copy and scan, it enters the termination protocol. It looks for work to steal and once it's done with that work it again enters the termination protocol. Termination attempt counts all the attempts to steal work.

 

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 – Timestamp when the individual GC worker stops.

GC worker time – Time taken by individual GC worker thread.

 

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 – The time (for each GC thread) that can't be attributed to the worker phases listed previously. Should be quite low. In the past, we have seen excessively high values and they have been attributed to bottlenecks in other parts of the JVM (e.g., increases in the Code Cache occupancy with Tiered).

 

Clear CT

[Clear CT: 0.6 ms]
Time taken to clear the card table of RSet scanning meta-data

 

Other

[Other: 6.8 ms]
Time taken for various other sequential phases of the GC pause.

 

CSet

[Choose CSet: 0.1 ms]
Time taken finalizing the set of regions to collect. Usually very small; slightly longer when having to select old.

 

Ref Proc

[Ref Proc: 4.4 ms]
Time spent processing soft, weak, etc. references deferred from the prior phases of the GC.

 

Ref Enq

[Ref Enq: 0.1 ms]
Time spent placing soft, weak, etc. references on to the pending list.

 

Free CSet

[Free CSet: 2.0 ms]
Time spent freeing the set of regions that have just been collected, including their remembered sets.

 

总结

(终于到总结了,好累)

在这篇OBE中,你已经了解到了Java JVM中的G1垃圾收集器了。你要了解的第一个是Heap和GC什么是任何Java JVM中的关键组件。接下来你要了解的是CMS和G1是如何工作的。接下来就是你要了解使用G1的时的命令行以及最佳实践。最后就是你要知道GC日志中记录对象和数据。

 

在这篇教程中,你已经学到了:

Java JVM中的组件

G1垃圾收集器的概览

CMS垃圾收集器的概览

G1的命令行和最佳实践

G1中的GC日志

资源

访问如下的网站可以获得更多的资源

Java HotSpot VM Options

The Garbage First(G1) Garbage Collector

Poonam Bajaj G1 GC Blog Post

Java SE 7: Develop Rich Client Applications

Java Performance - Charlie Hunt and Binu John

Oracle Learning Library

致谢

Curriculum Developer: Michael J Williams

QA: Krishnanjani Chitta

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值