1 G1(Garbage-First)收集器
1.1 总体概述
1.2 启动参数
1.3 基本概念
1.3.1 堆区布局
1.3.2 GC周期
1.3.3 GC暂停
1.3.4 收集集合(Collection Set)
1.3.5 记忆集合(Remembered Set)
1.3.6 收集过程
1.4 关键特性
1.4.1 堆空间分配
1.4.2 定期收集
1.4.3 确定堆初始化占比
堆初始化占比(IHOP ,Initiating Heap Occupancy Percent )是一个临界值,该值是指占用老年代堆区的百分比例,当实际老年代堆区占用比例达到此临界值,则触发并发开始收集操作(Concurrent Start collection)。
默认地,G1是自动地通过观察标记阶段所消耗的时间以及标记阶段期间老年代所占用的堆空间而确定一个优化的IHOP值,该特性被称之为自适应性IHOP。
如果自适应性IHOP特性被激活,在G1没有收集足够信息自动化地预测与确定IHOP值之前,使用-XX:InitiatingHeapOccupancyPercent的配置值作为IHOP的初始化值。
用户可以使用-XX:-G1UseAdaptiveIHOP关闭自适应性IHOP的特性,在这种情况下,IHOP值是由-XX:InitiatingHeapOccupancyPercent配置值确定。
一般情况下,G1会最大限度地先设置IHOP的初始值,从而保证在space-reclamation阶段中能正常启动混合式年轻代堆区的收集操作,其触发的条件是当IHOP值等于当前老年代堆区的总空间减去-XX:G1HeapReservePercent(预留的缓冲空间)配置值的占比空间。
1.4.4 标记操作
G1在标记阶段使用的算法是Snapshot-At-The-Beginning (SATB),快照技术是一种在存储系统中常用的技术,本质上是复制了一份数据副本,再对副本执行常用的操作。
同理,G1在标记暂停开始时生成一个堆区的虚拟快照,在整个标记过程中,G1只对快照进行标记操作,如果在标记开始时对象是存活的,则G1认为该对象在整个标记阶段中也是存活的,即使在标记过程中该对象的真实状态从存活变成了非存活,这是因为标记阶段包括并发标记的小阶段,在并发的情况下应用同时在执行业务逻辑,存活对象可能从存活变化为非存活,G1会在下一阶段中继续回收这些状态突变的对象。
SATB算法在理论上会浪费一些内存空间,但是使用该算法能提供更好的延迟水平,也就是请求响应的延迟更低。
1.4.5 堆空间高负载
堆空间高负载是指JVM的堆空间已经被消耗掉大部分,内存资源处于紧缺的状态,在这种情况下,G1找不到足够的空闲堆空间去保存被腾挪复制的存活对象,这是一种被称之为腾挪失败的异常。也就是,大部分对象已经被移动到新的堆空间中,但有部分对象还没有被移动(但引用关系已经更新了)。
这种异常一旦出现,G1立刻执行失败恢复操作,虽然恢复操作会给应用系统增加一些额外的开销,但是总体上不影响G1的正常运作。
如果失败恢复操作也失败,G1会启动完全GC操作(Full GC)。
此外,G1会使用一些预防措施最大限度地阻止腾挪失败异常的出现,例如,默认地执行回收大对象堆区的收集操作。当然,用户可以设置-XX:-UsePreventiveGC命令行参数关闭此预防措施功能。
1.4.6 大对象存储
大对象是指一个对象占用堆空间的大小大于或者等于堆单元区域大小的一半。
堆单元区域大小是由用户显示地使用-XX:G1HeapRegionSize命令行参数指定或者由JVM通过计算设置默认值。
G1使用以下的方式处理大对象的存储管理:
-
每个大对象占用老年代堆空间中的一系列连续单元区域(假如大对象占用很多个堆单元区域),对象的开始位置对应系列连续单元区域中的第一个区域的开始位置,而系列单元中的最后一个单元区域所剩下的堆空间将被浪费,直到对象被回收,这系列单元区域才被回收。
-
一般情况下,当大对象变成非存活对象,G1会在标记过程的最后小阶段Cleanup暂停时或者在Full GC时将其回收。但是,对于特殊的大对象,例如基本类型的大型数据集的数组,G1将在任何一个GC暂停中回收这类型没有被任何对象引用的数组。
-
由于一个大对象会消耗很多堆空间,所以,为大对象申请堆空间时可能会立刻触发一次GC操作,因为G1在为对象申请堆空间时,会检查前面章节提到的IHOP值,如果达到临界值,则立刻触发一次年轻代GC标记操作。
-
存活的大对象永远不会被腾挪,即使在Full GC时。这可能会进一步增加Full GC的暂停时间,也可能如果在堆区域中存在太多的内存碎片则会引起内存不足的异常。
1.4.7 默认配置
本章节主要提供一些重要的配置及其默认值。
配置项或者默认值 | 描述 |
-XX:MaxGCPauseMillis=200 | 最大GC暂停时间目标,默认值等于200 |
-XX:GCPauseTimeInterval=<ergo> | GC暂停阶段的时间间隔,不提供默认值 |
-XX:ParallelGCThreads=<ergo> | GC暂停阶段允许启动的最大并行工作线程数,取决于实际的计算机系统能提供的线程数N,N小于或等于8则取N值,N>8则取N*5/8 在开始几次的GC暂停中,G1限制最大的线程数,其由最大堆空间确定的,约等于最大堆空间除以-XX:HeapSizePerGCThread配置值 |
-XX:ConcGCThreads=<ergo> | GC并发执行阶段(例如并发标记阶段)的最大线程数,默认值等于-XX:ParallelGCThreads配置值除以4 |
-XX:+G1UseAdaptiveIHOP -XX:InitiatingHeapOccupancyPercent=45 | 自适应性IHOP开关打开时,G1使用自动化机制调整IHOP值,自适应性IHOP开关关闭时,G1使用默认指定值(不指定时默认值等于45) |
-XX:G1HeapRegionSize=<ergo> | 单元堆区域的大小,默认值约等于最大值堆空间除以2048,约等于32M。如果用户指定,则必须是2的次方,合法值的范围从1M到512M |
-XX:G1NewSizePercent=5 -XX:G1MaxNewSizePercent=60 | 其决定年轻代堆空间的最小初始化占比以及最大堆空间占比,默认最小值是5,默认最大值是60 |
-XX:G1HeapWastePercent=5 | 在space-reclamation阶段允许的未被回收的最大空闲空间占比(候选收集集合中的待回收空闲空间),低于此值,则立刻结束space-reclamation阶段 |
-XX:G1MixedGCCountTarget=8 | space-reclamation阶段的GC次数,默认值等于8 |
-XX:G1MixedGCLiveThresholdPercent=85 | 如果一个老年代堆区域的存活对象空间占比大于该值,G1不回收该区域 |
1.4.8 收集器类型对比
本章节描述G1收集器与其他类型收集器之间的主要区别:
-
并行收集器是一次性地对老年代堆空间进行空间压缩或者回收,而G1把GC操作分发成很多短暂的GC操作,这大量的短暂的GC暂停次数会降低吞吐率。
-
G1并发地执行一部分space-reclamation阶段的对老年代堆区的GC操作
-
G1因为并行性而增加系统的开销,从而降低系统的吞吐率
-
ZGC是面向拥有大容量内存资源的系统,其目的是提供更加短暂的GC暂停目标,但是以付出更多的吞吐率为代价。
此外,G1提供以下两种方法提升其GC效率:
-
G1在每次GC操作时都会回收完全空的区域以及大对象区域(使用-XX:-G1EagerReclaimHumongousObjects命令行参数关闭此功能,此功能默认是打开的)
-
G1能可选地并发地删除堆区的重复字符串(使用-XX:+G1EnableStringDeduplication命令行参数打开此功能,此功能默认是关闭的)
(未完待续)