1.Hotspot Architecture
Hotspot JVM体系结构支持强大的基本功能、实现高性能和提高可扩展性。
JVM的核心组件包括类加载程序、运行时数据区域和执行引擎。
其中,与性能相关的关键组件是Heap、JIT和Garbage收集器。一般来说,在调整Java应用程序时,主要focus有两个目标:响应时间和吞吐量。(Responsiveness和throughput)
2.G1 GC
G1 (garbage-first)是一种服务器端垃圾收集器,主要面向高存储和多核体系结构系统。它满足垃圾收集器对暂停时间的要求,同时实现高吞吐量。Jdk 7更新4开始支持G1 GC。设计目标如下:
CMS可以与应用程序线程(如)同时执行。压缩可用内存空间,但不像长GC那样暂停。需要更多可预测的GC间歇;我不想牺牲太多吞吐量。不需要太大的Java堆。G1计划用作CMS的长期更换计划。G1与CMS相比有一些变化,可以作为更好的方案。首先,G1压缩可用空间。G1压缩足以完全避免使用细分的空闲分配列表的问题,而是使用regions。这样可以简化一些收集器,并防止潜在的碎片问题。此外,G1提供了比CMS更可预测的暂停间隔,用户可以指定所需的暂停目标。
2.1 G1工作概述
旧垃圾收集器(serial、parallel和cms)将heap分为以下三部分:
G1收集器使用其他方法:
堆被分成大小相同的区域(regions),每个区域都是虚拟内存的连续范围。在某些region集合中,相同的角色——对应于以前收集器中的Eden、suvivor和old角色,但大小不固定。这样可以为内存使用提供更大的灵活性。
垃圾回收时,G1的工作方式与CMS在一定程度上相似。G1执行并行全局标记阶段,查找整个堆的生存对象。马克阶段完成后,G1发现regions可能是空的。首先收集这些regions,通常会生成大量可用空间。这就是我们称之为Garbage-First垃圾收集器的原因。与它的名称一样,G1将收集和压缩集中在可回收对象(即Garbage)可以填充的区域。此外,G1使用暂停预测模型来选择要执行收集的区域数,以符合用户定义的暂停时间目标。
G1为撤离准备了regions,加快了垃圾收集速度。为了压缩和释放内存,G1将对象从堆中的一个或多个regions复制到堆中的region。为了减少暂停时间和增加吞吐量,该撤离过程将并行运行。(evacuation)。因此,在每次垃圾收集中,G1在减少碎片的同时,继续工作,以避免超过用户定义的暂停时间。这超出了前面介绍的传统垃圾收集方法的范围。CMS垃圾收集器不进行压缩操作。ParallelOld垃圾收集器执行整个堆的压缩,但这可能会导致明显的停顿。
需要说明的是,G1不是实时垃圾收集器。它将尽最大努力与用户定义的暂停时间保持一致,但不一定能保证满意。G1根据历史收集数据估计在设置的暂停时间内可以收集的regions数。因此,G1有一个合理准确的收集regions所需时间的模型。使用此模型确定在指定停止时间内可以收集的regions数和regions数。
注:G1有一个并发(concurrent),它与应用线程(如refinement、marking和cleanup)并行(parallel、multi-threaded)运行,但FGC仍然在运行
2.2 G1 Footprint
从ParallelOldGC或CMS迁移到G1时,JVM进程size可能会增加。这个州
要与“accounting”数据结构有关,如Remembered Sets与Collection Sets。- Remembered Sets or RSets,追踪对象引用到指定region。堆中的每一个region都有一个RSet,通过RSet,可以对region进行并行、独立的收集。RSets对总footprint的影响小于5%。
- Collection Sets or CSets,这些region会在GC时被收集。CSet中的所有存活数据在GC时被疏散。这些regions可以是eden,suvivor或old generation。CSet对JVM大小的影响小于1%。
2.3 推荐使用G1的场景
G1主要致力于为使用大容量内存、但又期望有低GC延迟的应用提供一个解决方案。这意味着堆的大小要接近6G或更大,稳定或预测的停顿时间低于0.5秒。
当前使用了CMS或ParallelOldGC垃圾收集器的应用如果有以下的特性,将会从迁移到G1 GC中收益。
- FGC的持续时间太长或者太频繁;
- 对象分配的比例或promotion变化显著;
- 垃圾收集或压缩停顿时间长;
3. G1 GC分解
<1> G1堆结构
将一个内存区域切分成许多固定大小的regions。region的大小有JVM在启动的时候选择,JVM通常产生2000个左右的regions,大小为1~32mb。
<2> G1堆分配
实际上,这些region会被映射到eden,survivor和old generation。
如图所示,regions可以分配到eden,survivor与old generation regions。此外,还有第四种对象类型,也即“巨大”regions。这些regions用于存储大小是region的50%或更大的对象。他们存储为连续的regions集合。最后一种类型regions是堆的未使用区域。
注:截止到目前,回收大对象还没有得到充分优化,因此应该避免创建这种大小的对象。
<3> G1中的YGC
图中蓝色regions存的old generation对象,绿色regions存年轻代对象。
<4> G1的一次YGC
存活对象被疏散(拷贝或移动)到一个或多个survivor regions。 如果年龄门槛到了,则这些对象被promoted到old regions。
这个是STW阶段。为下一次YGC计算Eden大小和survivor大小。accounting信息用于辅助计算他们的大小,同时会考虑设置的停顿时间目标。
这个方法比较容易更改regions的大小,可以根据需要将regions调大或调小。
<5> G1 YGC的结尾
存活对象已经被疏散到survivor regions或old regions。
总结起来,G1的YGC有:
- 单个内存空间的堆被切分为regions;
- 年轻代的内存由一系列不连续的regions集合组成。因此可以很容易根据需要调整它们大小。
- YGC是STW事件。
- YGC是多线程并行进行的。
- 存活对象被拷贝到新的survivor或old regions。
5. G1 年老代收集
G1在old generation的堆上执行如下的阶段,其中有些阶段是YGC的一部分。
Phase | Description |
(1) Initial Mark (Stop the World Event) | This is a stop the world event. With G1, it is piggybacked on a normal young GC. Mark survivor regions (root regions) which may have references to objects in old generation. |
(2) Root Region Scanning | Scan survivor regions for references into the old generation. This happens while the application continues to run. The phase must be completed before a young GC can occur. |
(3) Concurrent Marking | Find live objects over the entire heap. This happens while the application is running. This phase can be interrupted by young generation garbage collections. |
(4) Remark (Stop the World Event) | Completes the marking of live object in the heap. Uses an algorithm called snapshot-at-the-beginning (SATB) which is much faster than what was used in the CMS collector. |
(5) Cleanup (Stop the World Event and Concurrent) |
|
(*) Copying (Stop the World Event) | These are the stop the world pauses to evacuate or copy live objects to new unused regions. This can be done with young generation regions which are logged as [GC pause (young)]. Or both young and old generation regions which are logged as [GC Pause (mixed)]. |
5.1 一步一步理解old generation GC
<1> Initial marking阶段
存活对象的mark在YGC之后进行,在GC日志中标注为GC pause(young)(inital-mark)
<2> concurrent marking阶段
如果发现空的regions(用”X”指示),则会立即移除。同时,计算用于判断存活的accounting信息。
<3> remark阶段
空regions被移除和回收。为所有的regions计算活跃度(liveness)。
<4> Copying/cleanup阶段
G1选择最低活跃度(liveness)的regions,这些regions可以快速回收。之后这些regions与YGC同时被回收。这个在GC日志中标记为[GC pause(mixed)]。所以年轻代与年老代同时被收集。
<5> copying/cleanup阶段之后
挑选的regions已经被收集与压缩到深蓝regions和深绿regions,如图所示:
年老代GC概述:
- concurrent marking phase
- 并发计算活跃度信息(应用正常运行);
- 通过活跃度信息可以找出那个regions更适合在疏散阶段回收;
- 与CMS不同,没有sweeping阶段;
- remark phase
- 使用snapshot-at-the-beginning(SATB)算法,速度比CMS使用的更快;
- 完全空的regions被回收;
- copying/cleanup阶段
- 年轻代与年老代被同时回收;
- 基于活跃度挑选年老代的regions;
6. 命令行选项与最佳实践
6.1 关键命令行开关
- -XX:+UseG1GC,让JVM使用G1垃圾收集器;
- -XX:MAXGCPauseMillis=200,设置期望的最大GC暂停时间。这个是软目标,JVM会尽最大努力实现它。因此,停顿时间目标有时候会无法满足。默认值是200毫秒。
- -XX:InitiatingHeapOccupancyPercent=45,开始异步GC周期的堆占用比例。G1触发并发GC周期使用的是整个堆的占用比例,不是某个generation。如果是0,则指示要不断的做GC。默认值是45。
6.2 最佳实践
使用G1的时候有一些最好要遵守的最佳实践。
<1> 不要设置年轻代的大小
通过-Xmn显示设置年轻代的大小会干预G1收集器的默认行为。
- G1在收集的时候将不再遵守停顿时间目标。因此,本质上设置了年轻代大小关闭了停顿时间目标。
- G1不再能根据需要扩展或收缩年轻代的空间。由于大小是固定的,因此无法改变。
<2> 响应时间度量(metric)
考虑设置-XX:MaxGCPauseMillis=<N>的值为目标停顿时间的90%或更多,而不是使用平均响应时间。这样,90%的请求用户都不会感觉到响应慢于设定的目标值。毕竟,停顿时间并不是总是能满足的。
<3> Evacuation Failure是什么?
当JVM在GC期间用完堆regions则会发生promotion failure。由于此时堆已经是最大了,所以没法扩展。如果配置了-XX:PrintGCDetails,此时GC日志会指示to-space overflow,这个failure是昂贵的,因为:
- GC依然要继续进行,因此空间必须要空出来;
- 失败的拷贝对象必须要被占有(have to be tunured in place);
- 对CSet中regions的RSets的任何更新都必须重新更新;
- 所有这些步骤的开销都是比较大的;
<4> 如何避免Evacuation Failure
- 增加堆尺寸
- 增加-XX:G1ReservePercent=n,默认值是10;
- G1创建一个虚拟的天花板(上限)来保持预留内存空闲,以防需要更多的”to-space”。
- 更早的开始marking周期;
- 增加marking的线程数,使用-XX:ConcGCThreads=n选项;
完整的G1 GC开关列表:
Option and Default Value | Description |
-XX:+UseG1GC | Use the Garbage First (G1) Collector |
-XX:MaxGCPauseMillis=n | Sets a target for the Maximum GC pause time. This is a soft goal, and the JVM will make its best effort to achieve it. |
-XX:InitiatingHeapOccupancyPercent=n | Percentage of the (entire) heap occupancy to start a concurrent GC cycle. It is used by GCs that trigger a concurrent GC cycle based on the occupancy of the entire heap, not just one of the generations ., G1). A value of 0 denotes 'do constant GC cycles'. The default value is 45. |
-XX:NewRatio=n | Ratio of new/old generation sizes. The default value is 2. |
-XX:SurvivorRatio=n | Ratio of eden/survivor space size. The default value is 8. |
-XX:MaxTenuringThreshold=n | Maximum value for tenuring threshold. The default value is 15. |
-XX:ParallelGCThreads=n | Sets the number of threads used during parallel phases of the garbage collectors. The default value varies with the platform on which the JVM is running. |
-XX:ConcGCThreads=n | Number of threads concurrent garbage collectors will use. The default value varies with the platform on which the JVM is running. |
-XX:G1ReservePercent=n | Sets the amount of heap that is reserved as a false ceiling to reduce the possibility of promotion failure. The default value is 10. |
-XX:G1HeapRegionSize=n | With G1 the Java heap is subdivided into uniformly sized regions. This sets the size of the individual sub-divisions. The default value of this parameter is determined ergonomically based upon heap size. The minimum value is 1Mb and the maximum value is 32Mb. |
1.《【applicationisinterrupted】G1 GC官方文件|最佳实践(翻译)》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。
2.《【applicationisinterrupted】G1 GC官方文件|最佳实践(翻译)》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。
3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/gl/2508274.html