Garbage-First(G1)垃圾收集器
简介
Garbage-First(G1)垃圾收集器面向多处理器机器设计,可扩展到大容量内存。它力图在高概率下满足垃圾回收的停顿时间目标,同时在几乎无需配置的情况下实现高吞吐量。G1 旨在在当前目标应用和环境中,在延迟与吞吐量之间提供最佳平衡,这些应用和环境具有以下特征:
- 堆大小可达数十 GB 或更大,且 Java 堆中超过 50% 被存活数据占用。
- 对象分配和晋升(promotion)的速率会随时间显著变化。
- 堆中存在大量内存碎片。
- 需要可预测的停顿时间目标,且停顿时间不超过几百毫秒,从而避免长时间的垃圾回收暂停。
G1 在应用程序运行的同时执行部分工作。它通过占用本可供应用程序使用的处理器资源,来换取更短的回收停顿时间。
这一点最明显地体现在:当应用程序运行时,会有一个或多个垃圾回收线程同时处于活跃状态。因此,与以吞吐量为优先的收集器相比,虽然 G1 的垃圾回收停顿通常更短,但应用程序的整体吞吐量也往往会略有降低。
G1 是默认的垃圾收集器。
G1 收集器通过多种方式实现高性能,并尝试满足停顿时间目标,这些方式将在后续章节中进行说明。
启用 G1
Garbage-First 垃圾收集器是默认收集器,因此通常不需要进行任何额外操作。你可以通过在命令行中添加 -XX:+UseG1GC 来显式启用它。
基本概念
G1 是一种分代的、增量式的、并行的、主要是并发的、会产生 Stop-The-World(STW)暂停的、并通过回收(evacuating)方式工作的垃圾收集器,它会在每次 STW 暂停中监控暂停时间目标。与其他收集器类似,G1 将堆划分为(虚拟的)新生代和老年代。空间回收主要集中在新生代,因为在该区域进行回收效率最高,同时也会偶尔在老年代进行空间回收。
某些操作始终在 Stop-The-World 暂停中执行,以提升吞吐量。而一些在应用程序停止时会耗时较长的操作,例如整个堆级别的操作(如全局标记),则会与应用程序并行、并发执行。为了在空间回收过程中保持较短的 STW 暂停,G1 以增量方式、分步骤并行地执行空间回收。G1 通过跟踪之前应用行为和垃圾回收暂停的信息来建立成本模型,从而实现可预测性,并使用这些信息来决定每次暂停中执行的工作量。例如,G1 会优先回收最有效率的区域(即主要充满垃圾的区域,因此得名 Garbage-First)。
G1 主要通过“疏散(evacuation)”来回收空间:将选定内存区域中的存活对象复制到新的内存区域中,并在此过程中对其进行压缩。在疏散完成后,原先被存活对象占用的空间会被应用程序重新用于分配。
Garbage-First 收集器不是实时收集器。它试图在较长时间范围内以较高概率满足设定的暂停时间目标,但对于单次暂停来说,并不能保证绝对确定地满足该目标。
堆布局
G1 将堆划分为一组大小相等的堆区域(heap regions),每个区域都是一段连续的虚拟内存范围,如图 7-1 所示。区域是内存分配和内存回收的基本单位。在任何给定时刻,这些区域可以是空的(浅灰色),或者被分配给某个特定的分代:新生代或老年代。当有内存请求到来时,内存管理器会分配空闲区域,并将其分配给某个分代,然后将其作为可用空间返回给应用程序,供其进行分配。

新生代包含 Eden 区域(红色)和 Survivor 区域(带 “S” 的红色)。这些区域的功能与其他收集器中的对应连续空间相同,但不同之处在于,在 G1 中这些区域在内存中通常以非连续的方式分布。老年代区域(浅蓝色)构成老年代。对于跨越多个区域的对象,老年代区域可能是巨型对象(humongous)(带 “H”的浅蓝色)。
应用程序始终在新生代中进行分配,也就是 Eden 区域中进行分配,但“巨型对象”除外,这类对象会直接在老年代中分配。
垃圾收集周期
从高层次来看,G1 收集器在两个阶段之间交替运行。仅年轻代阶段(young-only phase)包含垃圾回收操作,这些操作会逐步将当前可用内存填充为老年代中的对象。空间回收阶段(space-reclamation phase)则是在处理年轻代的同时,逐步回收老年代中的空间。随后,该周期会再次从仅年轻代阶段开始。

以下列表详细描述了 G1 垃圾收集周期的各个阶段、它们的停顿以及阶段之间的转换过程:
仅年轻代阶段(Young-only phase)
该阶段从若干次“普通年轻代收集(Normal young collections)”开始,这些收集会将对象逐步晋升到老年代。当老年代的占用率达到某个阈值(即“堆占用触发阈值 / Initiating Heap Occupancy threshold”)时,系统会从该阶段过渡到空间回收阶段(space-reclamation phase)。此时,G1 会调度一次“并发开始收集(Concurrent Start young collection)”,而不是普通的年轻代收集。
Concurrent Start(并发开始): 这种收集在执行普通年轻代收集的同时,还会启动标记(marking)过程。并发标记用于确定老年代区域中当前所有可达(存活)的对象,这些对象将在后续的空间回收阶段中被保留。 在标记尚未完全结束时,仍然可能发生普通的年轻代收集。标记最终通过两个特殊的 Stop-The-World(STW)停顿完成:Remark 和 Cleanup。
Concurrent Start 停顿也可能判断不需要继续进行标记:在这种情况下,会发生一个简短的并发标记撤销(undo)阶段,然后继续仅年轻代阶段。在这种情况下,不会发生 Remark 和 Cleanup 停顿。
Remark(重新标记): 该停顿用于最终完成标记本身,并执行引用处理和类卸载,回收完全空的区域,并清理内部数据结构。在 Remark 和 Cleanup 之间,G1 会计算后续在并发过程中回收选定老年代区域空间所需的信息,这些工作将在 Cleanup 阶段最终完成。
Cleanup(清理): 该停顿用于判断是否真的进入空间回收阶段。如果进入空间回收阶段,则仅年轻代阶段会以一次“准备混合收集(Prepare Mixed young collection)”结束。
空间回收阶段(Space-reclamation phase)
该阶段由多次年轻代收集组成,这些收集除了处理年轻代区域外,还会回收部分老年代区域中的存活对象。这些收集也称为“混合收集(Mixed collections)”。当 G1 判断继续回收更多老年代区域所带来的收益不足以抵消成本时,空间回收阶段结束。
在空间回收结束后,整个收集周期会重新回到新的仅年轻代阶段作为开始。
作为备用机制,如果在收集存活信息的过程中应用程序耗尽内存,G1 会像其他收集器一样执行一次原地的 Stop-The-World 全堆压缩(Full GC)。
垃圾收集停顿与收集集合(Collection Set)
G1 在 Stop-The-World(STW)停顿中执行垃圾收集和空间回收操作。存活对象通常会从源区域复制到堆中的一个或多个目标区域,同时对这些被移动对象的引用进行调整。
对于非巨型(non-humongous)区域,一个对象的目标区域是根据该对象所在的源区域来确定的:
- 新生代(年轻代)的对象(eden 和 survivor 区域中的对象)会根据其年龄被复制到 survivor 区域或老年代区域。
- 来自老年代区域的对象会被复制到其他老年代区域。
位于巨型(humongous)区域中的对象则以不同方式处理。G1 只会判断这些对象是否存活,如果不存活,则回收其占用的空间。G1 只有在非常慢的“最后手段”收集过程中才会移动巨型对象。
Remembered Set(记忆集)
为了对收集集合(collection set)中的区域进行疏散(evacuation),G1 维护了一个记忆集(remembered set):它记录了堆中位于收集集合之外、但包含指向收集集合内部引用的位置集合。当垃圾回收期间收集集合中的对象被移动时,所有来自收集集合外部、指向该对象的引用都必须被更新为指向对象的新位置。
记忆集条目使用近似位置来节省内存:通常来说,彼此接近的引用往往指向彼此接近的对象。G1 在逻辑上将堆划分为卡片(card),默认大小为 512 字节。记忆集条目是这些卡片索引的压缩表示。
G1 最初以“按区域(per-region)”的方式管理记忆集:每个区域都包含一个本地区域的记忆集,即可能包含指向该区域引用的位置集合。在垃圾回收期间,会基于这些区域记忆集生成整个收集集合的记忆集。
记忆集主要是延迟创建的:在 Remark 与 Cleanup 停顿之间,G1 会为所有收集集合候选区域重建其记忆集。除此之外,G1 始终维护年轻代区域的记忆集,因为它们在每次回收中都会被处理,并且默认情况下也会对部分巨型对象进行维护,以便进行主动回收。
Collection Set(收集集合)
收集集合是指用于回收空间的源区域集合。根据垃圾收集类型的不同,收集集合由不同类型的区域组成:
- 在仅年轻代阶段(Young-Only phase)中,收集集合仅包含年轻代区域,以及可能可以回收的巨型对象所在区域。
- 在空间回收阶段(Space-Reclamation phase)中,收集集合包含年轻代区域、可能可回收的巨型对象区域,以及来自候选集合中的部分老年代区域。
收集集合候选区域(collection set candidate regions)是那些在空间回收阶段中高度可能被回收的区域。G1 会在 Remark 停顿期间根据它们包含的存活数据量以及与其他区域的连接关系来选择这些区域。
- 存活数据少(即空闲空间多)的区域优先于大部分数据存活的区域
- 连接性低的区域优先于连接性高的区域
因为回收这些“更高效”的区域所需的工作量更小。G1 会从候选区域中剔除那些对可回收空间贡献不大的区域,例如那些可回收空间少于 -XX:G1HeapWastePercent 指定比例的区域。这些区域在本次空间回收阶段中将不会被回收。
在 Remark 与 Cleanup 停顿之间,G1 会开始为后续回收做准备,而 Cleanup 停顿则完成这些工作,并根据效率对区域进行排序。那些回收成本更低、同时可释放空间更多的区域,会在后续的 Mixed 收集中被优先回收。
垃圾收集过程(Garbage Collection Process)
一次垃圾收集由四个阶段组成:
Pre Evacuate Collection Set(疏散前准备阶段) 该阶段执行垃圾收集的一些准备工作:将 TLAB(线程本地分配缓冲区)从 mutator 线程中分离、根据《Java 堆大小调整》中描述的方式选择本次收集集合(collection set),以及其他一些较小的准备操作。
Merge Heap Roots(合并堆根) 在该阶段,G1 会从收集集合区域中创建一个统一的记忆集(remembered set),以便后续进行更容易的并行处理。这一步会去除各个独立记忆集中大量重复项,否则这些重复项需要在之后以更高成本进行过滤处理。
Evacuate Collection Set(疏散收集集合) 这是主要的工作阶段:G1 从根(roots)开始移动对象。根引用是指来自收集集合之外的引用,包括:
- JVM 内部数据结构中的引用(外部根 external roots)
- 代码中的引用(代码根 code roots)
- Java 堆其余部分中的引用(堆根 heap roots)
对于所有根引用,G1 会将收集集合中被引用的对象复制到其目标区域,并递归处理其引用,将指向收集集合内对象的引用作为新的根继续处理,直到不再存在新的根为止。
各个子阶段的执行时间可以通过 -Xlog:gc+phases=debug 日志观察,包括:Ext Root Scanning、Code Root Scan、Scan Heap Roots 和 Object Copy 等子阶段。
G1 还可能会为可选的收集集合重复执行主要的疏散阶段。
- Post Evacuate Collection Set(疏散后处理阶段) 该阶段包含清理工作,包括引用处理,以及为后续 mutator(应用线程)阶段做准备。
这些阶段对应于使用 -Xlog:gc+phases=info 时所打印的各个阶段。
Garbage-First 内部机制(Garbage-First Internals)
本节描述 Garbage-First(G1)垃圾收集器的一些重要细节。
Java 堆大小调整(Java Heap Sizing)
G1 在调整 Java 堆大小时遵循标准规则,使用以下参数:
-XX:InitialHeapSize:Java 堆初始大小-XX:MaxHeapSize:Java 堆最大大小-XX:MinHeapFreeRatio:用于确定最小空闲内存百分比-XX:MaxHeapFreeRatio:用于确定调整后最大空闲内存百分比
G1 垃圾收集器仅在 Remark(重新标记)停顿 和 Full GC(完整垃圾回收)停顿期间考虑根据这些选项调整 Java 堆大小。该过程可能会向操作系统释放内存,或从操作系统申请内存。
堆扩展发生在垃圾收集停顿期间,而内存释放则发生在停顿之后,并与应用程序并发执行。
仅年轻代阶段的分代大小调整(Young-Only Phase Generation Sizing)
G1 总是在一次普通的年轻代收集结束时,为下一个 mutator(应用线程)阶段确定年轻代的大小。通过这种方式,G1 可以基于对实际停顿时间的长期观察,满足使用 -XX:MaxGCPauseTimeMillis 和 -XX:GCPauseIntervalMillis 设置的停顿时间目标。
该计算会考虑相似大小的年轻代在以往回收中所花费的时间,例如包括以下信息:
- 在垃圾收集期间需要复制的对象数量
- 这些对象之间的相互关联程度
-XX:GCPauseIntervalMillis 和 -XX:MaxGCPauseTimeMillis 选项共同定义了最小应用程序利用率(Minimum Mutator Utilization, MMU)。G1 会尝试在每一个长度为 -XX:GCPauseIntervalMillis 的时间窗口内,使垃圾收集停顿时间最多占用 -XX:MaxGCPauseTimeMillis 毫秒。
如果没有其他约束,G1 会在 -XX:G1NewSizePercent 和 -XX:G1MaxNewSizePercent 所定义的范围之间自适应地调整年轻代大小,以满足停顿时间目标。有关如何修复长时间停顿的更多信息,请参见《Garbage-First 垃圾收集器调优》。
另外,也可以使用 -XX:NewSize 与 -XX:MaxNewSize 分别设置年轻代的最小和最大大小。
备注只指定其中一个后续选项(-XX:NewSize 或 -XX:MaxNewSize)会将年轻代大小固定为通过该参数传入的精确值。
这会禁用停顿时间控制(pause time control)。
空间回收阶段的分代大小调整(Space-Reclamation Phase Generation Sizing)
在空间回收阶段(space-reclamation phase)中,G1 会尝试在单次垃圾收集停顿中,最大化老年代空间的回收量。年轻代的大小通常会被设置为允许的最小值,一般由 -XX:G1NewSizePercent 决定,同时也会考虑 MMU(最小应用程序利用率)规范。
在该阶段的每一次 mixed 收集开始时,G1 会从收集集合候选区域中选择一部分区域加入到收集集合中。这些额外加入的老年代区域由三部分组成:
最小老年代区域集合 用于确保疏散(evacuation)进度的基本集合。该集合的大小由“收集集合候选区域数量 ÷ 空间回收阶段长度(由
-XX:G1MixedGCCountTarget决定)”来计算。额外老年代区域集合 如果 G1 预测在完成最小集合回收后仍然有剩余时间,则会从候选区域中继续加入老年代区域,直到预测使用约 80% 的剩余停顿时间为止。
可选收集集合(Optional collection set) 在完成前两部分之后,如果本次停顿仍然有时间,G1 会逐步疏散这部分可选区域。
前两个集合会在一次初始收集过程中完成,而可选集合则会在剩余时间内逐步处理。这种方式既保证了空间回收的进度,又提高了停顿时间控制的成功率,同时降低了可选集合管理带来的额外开销。
当收集集合候选区域不再有可用区域时,空间回收阶段结束。
更多关于 G1 使用多少老年代区域以及如何避免较长 mixed 收集停顿的信息,请参见《Garbage-First 垃圾收集器调优》。
周期性垃圾回收(Periodic Garbage Collections)
如果由于应用程序处于空闲状态而长时间没有发生垃圾回收,虚拟机可能会长时间持有大量未使用的内存,而这些内存本可以被其他用途使用。为了避免这种情况,G1 可以通过 -XX:G1PeriodicGCInterval 参数强制定期执行垃圾回收。
该参数用于指定 G1 进行垃圾回收的最小时间间隔(单位为毫秒)。如果自上一次垃圾回收停顿以来已经超过该时间,且当前没有并发回收周期正在进行,则 G1 会触发额外的垃圾回收,其可能行为如下:
- 在仅年轻代阶段(Young-Only phase)期间:
G1 会通过一次 Concurrent Start 停顿启动并发标记(concurrent marking);如果指定了
-XX:-G1PeriodicGCInvokesConcurrent,则会触发一次 Full GC。 - 在空间回收阶段(Space Reclamation phase)期间: G1 会继续空间回收阶段,并触发与当前进度相匹配的垃圾回收停顿类型。
-XX:G1PeriodicGCSystemLoadThreshold 参数可以用于进一步限制是否触发周期性垃圾回收:如果 JVM 所在系统(例如容器)通过 getloadavg() 返回的一分钟平均系统负载高于该阈值,则不会执行周期性垃圾回收。
更多关于周期性垃圾回收的信息,请参见JEP 346:《Promptly Return Unused Committed Memory from G1》。
确定初始堆占用率(Determining Initiating Heap Occupancy)
初始堆占用率百分比(Initiating Heap Occupancy Percent,IHOP)是触发一次 Concurrent Start 收集的阈值,它被定义为老年代大小的一个百分比。
G1 默认会通过观察标记(marking)所花费的时间,以及在标记周期中老年代通常的内存分配情况,自动计算一个最优的 IHOP。这一特性称为 自适应 IHOP(Adaptive IHOP)。如果该功能处于启用状态,那么 -XX:InitiatingHeapOccupancyPercent 选项仅在系统还没有足够观测数据以准确预测 IHOP 阈值时,作为初始值使用,其含义是当前老年代大小的一个百分比。可以通过 -XX:-G1UseAdaptiveIHOP 关闭这一行为。在这种情况下,-XX:InitiatingHeapOccupancyPercent 的值将始终决定该触发阈值。
在内部实现中,自适应 IHOP 的目标是将“初始堆占用率”设置为:当老年代占用达到一个值时,空间回收阶段的第一次 mixed 垃圾回收刚好开始。这个目标值通常等于当前最大老年代容量减去 -XX:G1HeapReservePercent 所预留的额外缓冲空间。
标记(Marking)
G1 的标记使用一种称为 “起始快照(Snapshot-At-The-Beginning,SATB)” 的算法。它会在初始标记(Initial Mark)停顿时对堆创建一个虚拟快照,在标记开始时处于存活状态的所有对象,在整个标记过程中都被认为是存活的。这意味着,在标记过程中变为死亡(不可达)的对象,在空间回收时仍然会被当作存活对象处理(某些情况除外)。与其他收集器相比,这可能会导致一些额外的内存被“错误保留”。然而,SATB 可以在一定程度上提供更好的延迟表现,尤其是在 Remark 停顿期间。那些在标记期间被过于保守地认为存活的对象,会在下一次标记过程中被回收。
关于标记相关问题的更多信息,请参见《Garbage-First 垃圾收集器调优》。
极度紧张堆空间情况下的行为(Behavior in Very Tight Heap Situations)
当应用程序长期保持大量对象存活,以至于疏散(evacuation)无法找到足够空间进行对象复制时,就会发生 疏散失败(Evacuation Failure)。疏散失败意味着 G1 会尝试完成当前垃圾收集:已经成功移动的对象保持在新位置,而尚未移动的对象不会再被复制,只是对对象之间的引用进行调整。疏散失败可能会带来一定额外开销,但通常其速度仍接近一次普通的年轻代收集。在发生该失败的垃圾收集完成后,G1 会正常恢复应用程序执行,而不会立即采取其他措施。G1 会假设疏散失败发生在垃圾收集接近尾声时,即大部分对象已经被移动,并且剩余空间足够支撑应用继续运行,直到标记完成并进入空间回收阶段。
如果这一假设不成立,那么 G1 最终会触发一次 Full GC。这种收集会对整个堆进行原地压缩,可能非常缓慢。
关于分配失败或 Full GC 早于 OOM 发生的问题,请参见《Garbage-First 垃圾收集器调优》。
巨型对象(Humongous Objects)
巨型对象是指大小大于或等于一个 Region(区域)一半的对象。当前 Region 的大小会根据 G1 的“自动调优默认值(Ergonomic Defaults for G1 GC)”来确定,除非通过 -XX:G1HeapRegionSize 参数显式设置。
这些巨型对象在某些情况下会被特殊处理:
分配方式 每个巨型对象都会被分配到老年代中一段连续的 Region 序列中。对象本身的起始位置总是位于该序列中第一个 Region 的开头。该序列中最后一个 Region 可能存在剩余空间,但这部分空间在整个对象被回收之前无法用于其他分配。
回收方式 通常情况下,巨型对象只能在标记结束后的 Cleanup 停顿中被回收,或者在 Full GC 中如果它们已不可达时被回收。 但对于某些特殊情况(例如 primitive 类型数组,如 bool、各种整数类型以及浮点数数组),G1 有一个特殊优化:在任何垃圾回收停顿中,如果这些巨型对象没有被大量对象引用,G1 会尝试主动回收它们。该行为默认开启,可以通过
-XX:G1EagerReclaimHumongousObjects关闭。分配对 GC 的影响 巨型对象的分配可能会导致垃圾回收提前发生。G1 在每次巨型对象分配时都会检查 IHOP(Initiating Heap Occupancy)阈值,如果当前堆占用超过该阈值,则可能立即触发一次 Initial Mark young collection。
移动行为(非常重要) 巨型对象只有在极端情况下才会被移动: 当第一次 Full GC 仍无法为巨型对象分配足够的连续空间时,G1 会在同一次停顿中触发第二次 Full GC 进行“最后手段”的回收尝试。这个过程非常慢。由于包含巨型对象尾部的 Region 可能存在不可用空间,即使经过这些处理,仍然可能导致 JVM 因为内存不足而退出(OutOfMemoryError)。
G1 GC 的自适应默认值(Ergonomic Defaults for G1 GC)
本主题概述了 G1 中最重要的一些默认配置及其默认取值。这些默认值在不使用任何额外参数的情况下,提供了对 G1 行为和资源使用情况的基本预期。
| 选项与默认值 | 说明 |
|---|---|
-XX:MaxGCPauseMillis=200 | 最大停顿时间目标。 |
-XX:GCPauseTimeInterval=<ergo> | 最大停顿时间间隔目标。默认情况下 G1 不设置该目标,允许在极端情况下连续执行垃圾回收(back-to-back)。 |
-XX:ParallelGCThreads=<ergo> | 垃圾回收停顿期间用于并行工作的最大线程数。该值由 JVM 运行所在机器的可用线程数推导而来:如果可用 CPU 线程数小于等于 8,则直接使用该值;否则使用“超过 8 的线程数的五分之八”加上 8 作为最终线程数。在每次停顿开始时,该线程数还会受到最大堆大小限制:G1 不会为每 -XX:HeapSizePerGCThread 大小的 Java 堆容量分配超过一个线程。 |
-XX:ConcGCThreads=<ergo> | 并发标记阶段使用的最大线程数。默认值为 -XX:ParallelGCThreads / 4。 |
-XX:+G1UseAdaptiveIHOP -XX:InitiatingHeapOccupancyPercent=45 | 控制初始堆占用率(IHOP)的默认设置:启用自适应 IHOP;在最初几个收集周期中,G1 会使用老年代 45% 的占用率作为标记起始阈值。 |
-XX:G1HeapRegionSize=<ergo> | 堆区域(Region)的大小。默认值基于最大堆大小计算,目标是生成约 2048 个区域,最大值经过调优不超过 32 MB。用户指定值必须是 2 的幂,合法范围为 1 MB 到 512 MB。 |
-XX:G1NewSizePercent=5 -XX:G1MaxNewSizePercent=60 | 年轻代大小的范围,占当前 Java 堆的百分比,在这两个值之间动态变化。 |
-XX:G1HeapWastePercent=5 | 收集集合候选区域中允许未回收空间的比例(百分比)。当候选区域中的可回收空间低于该值时,G1 会停止空间回收阶段。 |
-XX:G1MixedGCCountTarget=8 | 空间回收阶段的期望长度,以垃圾收集次数(collections)为单位。 |
-XX:G1MixedGCLiveThresholdPercent=85 | 老年代区域中存活对象比例超过该百分比的区域,不会在本次空间回收阶段被收集。 |
备注
<ergo>表示该实际取值是根据运行环境通过“自适应调优(ergonomics)”自动决定的。
与其他收集器的比较(Comparison to Other Collectors)
这是 G1 与其他垃圾收集器之间主要差异的总结:
- **Parallel GC(并行收集器)**只能在一次整体操作中压缩并回收老年代空间。相比之下,G1 将这项工作分摊到多次更短的收集过程中逐步完成。这显著缩短了停顿时间,但可能会以吞吐量下降为代价。
- G1 会在并发过程中执行部分老年代空间回收工作。
- 由于其并发特性,G1 可能比上述收集器具有更高的开销,从而影响整体吞吐量。
- ZGC 的目标是提供更小的停顿时间,但代价是进一步降低吞吐量。
由于其工作方式,G1 具有一些机制来提升垃圾回收效率:
- G1 可以在任意一次垃圾回收中回收老年代中一些完全空的大区域。这可以避免许多不必要的垃圾回收,在较低开销下释放大量空间。
- G1 可以选择性地在 Java 堆中并发去重重复字符串。
回收老年代中完全空的大对象(humongous objects)的机制始终是启用的。可以通过 -XX:-G1EagerReclaimHumongousObjects 禁用该功能。
字符串去重默认是关闭的,可以通过 -XX:+UseStringDeduplication 启用。