G1的原理整理

有道云笔记

G1垃圾收集器是JDK7 update 4(2011年7月7日)引入的一款垃圾收集器,全称Garbage-First Garbage Collector,G1是一个分代的,增量的,并行与并发的标记-复制垃圾回收器。它的设计目标是为了适应现在不断扩大的内存和不断增加的处理器数量,进一步降低暂停时间(pause time),同时兼顾良好的吞吐量。

JDK8新特性(2014年初)(LTS版本)

JVM使用MetaSpace代替了永久代(PermGen Space)

JDK9新特性(2017年9月)

设置G1为JVM默认垃圾收集器

JDK10新特性(2018年3月)

并行Full GC,来优化G1的延迟

允许在不执行全局VM安全点的情况下执行线程回调,可以停止单个线程,而不需要停止所有线程或不停止线程

重要特性:通过var关键字实现局部变量类型推断,使Java语言变成弱类型语言、JVM的G1垃圾回收由单线程改成多线程并行处理,降低G1的停顿时间

JDK11新特性(2018年9月)(LTS版本)

1、ZGC,ZGC可以看做是G1之上更细粒度的内存管理策略。由于内存的不断分配回收会产生大量的内存碎片空间,因此需要整理策略防止内存空间碎片化,在整理期间需要将对于内存引用的线程逻辑暂停,这个过程被称为"Stop the world"。只有当整理完成后,线程逻辑才可以继续运行。(并行回收)

目标是GC暂停时间不会超过10ms,既能处理几百兆的小堆,也能处理几个T的大堆。

JDK12新特性(2019年3月)

Shenandoah GC,新增的GC算法,低暂停时间垃圾收集器(实验性)

G1收集器的优化,将GC的垃圾分为强制部分和可选部分,强制部分会被回收,可选部分可能不会被回收,提高GC的效率

JDK13新特性(2019年9月)

ZGC优化,将标记长时间空闲的堆内存空间返还给操作系统,保证堆大小不会小于配置的最小堆内存大小,如果堆最大和最小内存大小设置一样,则不会释放内存还给操作系统;

JDK14新特性(2020年3月)

JDK15新特性(2020年9月)

ZGC (JEP 377) 和Shenandoah (JEP 379) 不再是实验性功能。默认的 GC 仍然是G1。

JDK16新特性(2021年3月)

1、ZGC性能优化

增强ZGC,ZGC获得了 46个增强功能 和25个错误修复,控制stw时间不超过10毫秒

JDK17新特性(2021年9月)(LTS版本)

虽然JDK17也是一个LTS版本,但是并没有像JDK8和JDK11一样引入比较突出的特性,主要是对前几个版本的整合和完善

JDK8 到 JDK17 各个版本的重要特性一览:

Java8到Java17都更新了哪些内容?!收藏了 - 知乎

 

CMS垃圾回收器有两个比较显著的问题,一个是长时间运行无法避免Full GC,一个是Remark阶段STW时间较长。正是因为这两个问题的存在,CMS垃圾回收器在JDK9被标记弃用。

G1需要暂停来拷贝对象,而CMS在暂停中只需要扫描(mark)对象,那算法上G1的暂停时间会比CMS短么?

其实CMS在较小的堆、合适的workload的条件下暂停时间可以很轻松的短于G1。在2011年的时候Ramki告诉我堆大小的分水岭大概在10GB~15GB左右:以下的-Xmx更适合CMS,以上的才适合试用G1。现在到了2014年,G1的实现经过一定调优,大概在6GB~8GB也可以跟CMS有一比,我之前见过有在-Xmx4g的环境里G1比CMS的暂停时间更短的案例。

G1采用了开创性的局部收集的设计思路和以Region为基本单位的内存布局方式,它将Java堆空间划分成多个大小相等的独立区域(Region),JVM目标是总共不超过2048个Region(由JVM源码参数TARGET_REGION_NUMBER定义),虽然可以超过该值,但不推荐。

通常Region的大小等于堆空间总大小除以Region的个数,比如堆空间大小为4096MB,总共有2048个Region,那么每个Region的大小为2MB,也可以通过参数-XX:G1HeapRegionSize来指定Region的大小,假设参数值为4MB,那么堆空间就只有1024个Region了,一般推荐默认的计算方式。

G1将所有Region分为四种类型:Eden、Survivor、Old、Humongous。

 CSet

  Collection Set,简称CSet。在垃圾收集过程中收集的Region集合可以称为收集集合(CSet),也就是在垃圾收集暂停过程中被回收的目标。GC时在CSet中的所有存活数据都会被转移,分区释放回空闲分区队列

  见下图,左边的年轻代收集CSet代表年轻代的一部分分区,右边的混合收集CSet代表年轻代的一部分区和老年代的多个分区:

RememberedSet(RSet)和Card Table

对象跨代引用的问题。比如YGC阶段标记新生代对象的时候,不仅需要扫描GC Roots标记其直接引用的新生代对象,还需要扫描所有老年代对象,查看其是否引用了新生代对象,如果引用了的话,对应新生代对象也是需要标记的,否则就会发生漏标。然而实际实现中,扫描整个老年代对象的代价相当大。文章中我们介绍了可以通过Card Table这种数据结构大幅降低扫描老年代对象查找跨代引用的代价,简单来说,就是通过写屏障技术在老年代对象引用新生代对象的时候将Card Table对应Card设置成"脏卡",这样的话,查找跨代引用,只需要扫描Card Table脏卡对应的老年代区域,而不需要扫描整个老年代,大大降低了扫描代价。

根据上文介绍,G1也是一个分代回收器,也需要处理跨代引用扫描代价大的问题。为了解决这个问题,G1引用了另一种新的数据结构 - Remembered Set,简称RSet。

与CMS回收器中Card Table相比,Card Table中的脏卡代表"我引用了别的对象",属于一种points-out结构。而RSet记录的是哪些其他Region引用了本Region中的对象,属于points-into结构。两者是有本质区别的。

如果一个Region是热点Region,表示这个Region中的对象被大量其他Region中的对象所引用,那么RSet占用的内存空间开销就会越大。但我们要清楚一点,RSet内存开销不是为业务服务的,实际上不应该占用太多。因此为了控制RSet占用内存空间的大小,RSet会根据引用Region个数的多少,设置3种不同的实现方式(上文中介绍的是其中一种),分别称为:

  • sparse per-region-table (PRT),从字面意思来看表示这个RSet是一个稀疏的集合。具体实现使用HashMap方式记录引用关系,其中Map的key是引用Region,value是一个List,List中存储引用Region中的引用Card列表。上文有过介绍。
  • fine-grained PRT,还是使用HashMap方式记录引用关系,其中Map的key是引用Region,但value不再是List,而是一个bitmap,bit位为1表示对应Card是引用Card,否则不是引用Card。
  • coarse-grained bitmap,从字面意思可以看出来这就是一个bitmap,不过bitmap中每个bit位引用粒度不再是Card,而是Region。如果bit位值为1,表示这个Region是引用Region,即这个Region中有对象引用了该Region中的对象。

很显然,上述3种实现方式中,spase PRT和fine-grained PRT都是精确到Card,而coarse-grained bitmap是精确到Region。

Young GC核心流程

和"ParNew+CMS"一样,一旦Eden区满了之后,就会触发YGC。YGC只负载回收堆中新生代的所有Region,不回收老年代的Region。基本流程可以表示为如下几步:

(1)将eden区和survivor区所有Region添加到CSet中准备回收。注:CSet是一个存放待回收Region的数据结构。

(2)标记阶段

  • 从GC Roots开始标记直接引用的新生代对象。
  • 基于RSet标记跨代引用的新生代对象。

(3)复制阶段:将标记活跃的对象复制到其他Region中。

Concurrent Marking Cycle核心流程

根据上文介绍,一旦"JVM已使用内存/总内存"的比例超过设定阈值IHOP(InitiatingHeapOccupancyPercent)后,G1会执行一次Concurrent Marking Cycle,并在之后进行多轮Mixed GC。和CMS回收器基本一样,G1中一轮并发标记周期包含初始标记、并发标记、重新标记,再加上一个cleanup阶,这样就可以完成整堆所有Region的对象标记。下面是一段G1回收器中并发标记周期的完整日志:

  • 初始标记(Initial Marking):初始化标记是伴随一次普通的YGC发生的,从GC Root开始标记直接可达的对象。
  • 并发标记( Concurrent Marking):这个阶段标记线程和应用线程并发工作,遍历整堆所有可达对象并标记。这个阶段需要特别关注并发标记可能产生的"漏标"问题,G1使用Snapshot AT Begining(简称SATB)算法避免漏标问题发生,这和CMS完全不同。3.4小节深入介绍。
  • 重新标记( Remark) :标记那些在并发标记阶段发生变化的对象,将被回收。
  • 清理( Cleanup ):释放没有存活对象的Region。

Mixed GC核心流程

一轮并发标记之后紧接着是混合回收周期,包括多次Mixed GC,每次Mixed GC只回收部分Region。这里有三个问题:

(1)为什么这些Region需要分为多次回收?

主要原因是所有Region一起回收的话有可能会导致暂停时间比较长,尤其在内存较大的情况下。为了每次回收的暂停时间可控,就将一次大回收分成很多次小回收。(2)每次回收Region集合的选择原则是什么?

在执行混合回收之前,G1会将所有Region按照每个Region中垃圾对象占比进行一次排序,垃圾对象占比越高,排名越靠前。排好序之后,每次Mixed GC按顺序选择部分Region进行回收,选择多少Region取决于这些Region的回收预估暂停时间不超过设置的最大暂停时间。这个回收算法称为Garbage First,也就是G1的意思。

(3)每次Mixed GC的过程是怎么样的?

具体的回收过程和上文中介绍的Young GC没有太大区别,可以参考上文介绍。

在筛选回收阶段对各个Region进行回收价值和成本进行排序,这句话怎么理解?

比如现在有Region1、Region2和Region3三个区域,其中Region1预计可以回收1.5MB内存,预计耗时2ms;Region2预计可以回收1MB内存,预计耗时1ms;Region3预计可以回收0.5MB内存,预计耗时1ms。那么Region1、Region2和Region3各自的回收价值与成本比值分别是:0.75、1和0.5。

比值越高说明同样的付出,收益越高,如果此时只能回收一个Region的内存空间,G1就会选择Region2进行回收。这种方式保证了G1收集器在有限的时间内尽可能地提高收集效率。

它默认的停顿目标为两百毫秒, 一般来说, 回收阶段占到几十到一百甚至接近两百毫秒都很正常, 但如果把停顿时间调得非常低, 譬如设置为二十毫秒, 很可能出现的结果就是由于停顿目标时间太短, 导致每次选出来的回收集只占堆内存很小的一部分, 收集器收集的速度逐渐跟不上分配器分配的速度, 导致垃圾慢慢堆积。 很可能一开始收集器还能从空闲的堆内存中获得一些喘息的时间, 但应用运行时间一长就不行了, 最终占满堆引发Full GC反而降低性能, 所以通常把期望停顿时间设置为一两百毫秒或者两三百毫秒会是比较合理的。

并发标记阶段对象"漏标"问题解法 - SATB算法

在介绍SATB算法之前,先简单介绍一下并发标记过程中可能出现的对象"漏标"问题。

对象漏标简介

什么是对象漏标?就是本应该被标记为活跃的对象因为某些原因最终没有被标记,被垃圾回收器认为是垃圾对象而回收掉,最终导致应用错误,很显然这种情况是不允许发生的。

并发标记阶段什么场景下会发生对象漏标?应用线程和标记线程并发执行,在下面两种场景下会出现漏标:

1. 应用线程在并发标记过程中新生成的活跃对象因为某些原因没有被标记线程标记。

2. 应用线程在并发标记过程中变更引用关系的时候在特定场景下会出现漏标,具体场景涉及标记算法"三色标记法",

如上图所示,老年代中有三个对象A、B和C,在标记之前的引用关系是A引用B,B引用C。先分别介绍一下他们的标记情况:

  • 对象A已经被标记为黑色,表示为A为活跃对象且所有它引用的对象也完成标记。
  • 对象B被标记为灰色,表示B对象是活跃对象,但是它关联的对象还没有被完全标记完。
  • 对象C是白色,表示还没有被标记。

在这种背景下,应用线程此时发生了一次引用关系变更,B引用C的关系被删除了且同时A引用了C,即如下所示代码:

objB.fieldC = null; objA.filedC = C;

此时,标记线程就不再会标记对象C,因为对象A已经是黑色,表示所有它引用的对象都已经完成标记。然而实际上活跃的对象C就会被漏标最终被回收掉。

SATB算法思想简介

对象漏标介绍之后简单介绍一下SATB算法的基本思想,可以概括为如下三句话:

  1. 并发标记之前先给Region内存打个快照,标记线程基于这个快照独立进行标记。应用线程不会直接修改这个快照中的对象,也就是说应用线程不会干扰标记线程的工作。
  2. 应用线程新分配的对象都认为是活跃对象,实际在下一个并发标记周期进行标记。上文说过漏标发生的第一种场景是"应用线程在并发标记过程中新生成的活跃对象因为某些原因没有被标记线程标记",那如果能够将标记阶段新分配的对象全都集合到一起,这些对象全部都标记为活跃对象(实际肯定会有部分垃圾对象,将垃圾对象标记为活跃对象不影响程序正确性)就可以解决这个问题。
  3. 并发标记过程中已存在对象的引用关系变更在Remark阶段单独进行处理。上文介绍了漏标发生的第二种场景,为了解决这个场景引入的漏标问题,可以将引用关系变更分解为旧的引用关系先删除,新的引用关系生成两个步骤,只要破坏任何一个步骤就可以防止漏标发生。因此有两种针对性解法:
    • 在并发标记阶段如果有新引用关系生成,就记录下来,Remark阶段进行重标记,这个破坏了步骤二,即黑色对象重新引用了白色对象,就记录下来重新扫描黑色对象,将其引用的所有对象都标记成存活对象。这个就是CMS垃圾回收器使用的增量更新算法。
    • 在并发标记阶段如果有引用关系被删除,就记录下来,Remark阶段对这些引用关系被删除的重标记,这个破坏了步骤一,即灰色对象断开了白色对象引用的时候,记录下来,后面重新把这个白色对象标记成存活对象。这个就是G1垃圾回收器使用的算法。

简单来说,SATB明确将并发标记这一个大工程分成了三个字模块,分别是对快照进行并发标记、对并发标记过程中新分配的对象全部标记为活跃、对并发标记过程中引用关系变更的对象单独进行处理。

SATB算法 vs Incremental Update算法

G1的SATB算法在Remark阶段不需要暂停遍历整堆对象,所以避免了这个阶段可能的长耗时。但是CMS垃圾回收器中增量更新算法因为无法知道哪些对象是并发标记阶段新增的,所以在Remark阶段需要重新扫描GC Roots标记整堆对象,这就可能带来不可控的长耗时暂停。

G1 GC的分类和过程 JDK10 之前的G1中的GC只有YoungGC,MixedGC。FullGC处理会交给单线程的Serial Old垃圾收集器。

在jdk10版本的G1 GC会有很多优化。Full CG方面,将提供并发标记的Full GC方案:Parallelize Mark-Sweep-Compact。Card Table的扫描也会得到加速。RSet也优化了,目前的RSet会存储在所有的分区里,新版本的RSet只需要在CSet中,并且是在Remark到Clean阶段之间并发构建RSet。这项优化会增加整个并发标记的周期,但是缩减了很多RSet的占用空间。另外,对于PauseTime会有更精准的处理,在MixedGC的对象拷贝阶段,提供了可放弃拷贝的(Abortable)选项。MixedGC会计算下一个Region的对象拷贝,如果可能会超过预期的pause time,则会放弃这次拷贝。

G1源码之fullGC

Full GC的入口在g1CollectedHeap.cpp的G1CollectedHeap::do_full_collection

//参数分别是: //1.是否是明确的gc即为false则表示gc应最少满足回收word_size的大小,为true则表示system.gc或整个堆回收 //2.是否清除所有软引用 //3.要申请的空间大小 bool G1CollectedHeap::do_full_collection(bool explicit_gc, bool clear_all_soft_refs, bool do_maximal_compaction) { assert_at_safepoint_on_vm_thread(); if (GCLocker::check_active_before_gc()) { // Full GC was not completed. return false; } const bool do_clear_all_soft_refs = clear_all_soft_refs || soft_ref_policy()->should_clear_all_soft_refs(); G1FullGCMark gc_mark; GCTraceTime(Info, gc) tm("Pause Full", NULL, gc_cause(), true); G1FullCollector collector(this, explicit_gc, do_clear_all_soft_refs, do_maximal_compaction, gc_mark.tracer()); collector.prepare_collection(); collector.collect(); collector.complete_collection(); // Full collection was successfully completed. return true; }

  • 准备回收,prepare_collection
  • 回收,collect
  • 回收后处理,complete_collection

 

JEP 307: Parallel Full GC for G1

Java12新特性 -- 增强G1,自动返回未用堆内存给操作系统

Java 12 中增强了 G1 垃圾收集器关于混合收集集合的处理策略,这节主要介绍在 Java 12 中同时也对 G1垃圾回收器进行了改进,使其能够在空闲时自动将 Java 堆内存返还给操作系统,这也是 Java 12 中的另外一项重大改进。

目前 Java 11 版本中包含的 G1 垃圾收集器暂时无法及时将已提交的 Java 堆内存返回给操作系统。为什么呢? G1目前只有在full GC或者concurrent cycle(并发处理周期)的时候才会归还内存,由于这两个场景都是G1极力避免的,因此在大多数场景下可能不会及时归还committed Java heap memory给操作系统。除非有外部强制执行。

在使用云平台的容器环境中,这种不利之处特别明显。即使在虚拟机不活动,但如果仍然使用其分配的内存资源,哪怕是其中的一小部分,G1 回收器也仍将保留所有已分配的 Java 堆内存。而这将导致用户需要始终为所有资源付费,哪怕是实际并未用到,而云提供商也无法充分利用其硬件。如果在此期间虚拟机能够检测到 Java 堆内存的实际使用情况,并在利用空闲时间自动将 Java 堆内存返还,则两者都将受益。

  • 为了尽可能的向操作系统返回空闲内存,G1 垃圾收集器将在应用程序不活动期间定期生成或持续循环检查整体 Java堆使用情况,以便 G1 垃圾收集器能够更及时的将 Java 堆中不使用内存部分返还给操作系统。对于长时间处于空闲状态的应用程序,此项改进将使 JVM 的内存利用率更加高效。而在用户控制下,可以可选地执行Full GC,以使返回的内存量最大化。JDK12的这个特性新增了两个参数分别是G1 PeriodicGCInterval及G1 PeriodicGCSystemLoadThreshold,设置为0的话,表示禁用。如果应用程序为非活动状态,在下面两种情况任何一个描述下,G1 回收器会触发定期垃圾收集:自上次垃圾回收完成以来已超过 G1PeriodicGCInterval ( milliseconds ), 并且此时没有正在进行的垃圾回收任务。如果 G1PeriodicGCInterval 值为零表示禁用快速回收内存的定期垃圾收集。应用所在主机系统上执行方法 getloadavg(),默认一分钟内系统返回的平均负载值低于G1PeriodicGCSystemLoadThreshold指定的阈值,则触发full GC或者concurrent GC( 如果开启G1PeriodicGCInvokesConcurrent ),GC之后Java heap size会被重写调整,然后多余的内存将会归还给操作系统。如果 G1PeriodicGCSystemLoadThreshold 值为零,则此条件不生效。如果不满足上述条件中的任何一个,则取消当期的定期垃圾回收。等一个 G1PeriodicGCInterval 时间周期后,将重新考虑是否执行定期垃圾回收。G1 定期垃圾收集的类型根据 G1PeriodicGCInvokesConcurrent 参数的值确定:如果设置值了,G1 垃圾回收器将继续上一个或者启动一个新并发周期;如果没有设置值,则 G1 回收器将执行一个Full GC。在每次一次 GC 回收末尾,G1 回收器将调整当前的 Java 堆大小,此时便有可能会将未使用内存返还给操作系统。新的 Java 堆内存大小根据现有配置确定,具体包括下列配置:- XX:MinHeapFreeRatio、-XX:MaxHeapFreeRatio、-Xms、-Xmx。默认情况下,G1 回收器在定期垃圾回收期间新启动或继续上一轮并发周期,将最大限度地减少应用程序的中断。如果定期垃圾收集严重影响程序执行,则需要考虑整个系统 CPU 负载,或让用户禁用定期垃圾收集。

其他性能优化:

https://tschatzl.github.io/

https://tschatzl.github.io/2020/09/01/jdk15-g1-parallel-gc-changes.html

JEP 248: Make G1 the Default Garbage Collector

Limiting GC pause times is, in general, more important than maximizing throughput. Switching to a low-pause collector such as G1 should provide a better overall experience, for most users, than a throughput-oriented collector such as the Parallel GC, which is currently the default.

Many performance improvements were made to G1 in JDK 8 and its update releases, and further improvements are planned for JDK 9. The introduction of concurrent class unloading (JEP 156) in JDK 8u40 made G1 a fully-featured garbage collector, ready to be the default.

通常,限制GC暂停时间比最大化吞吐量更重要。对于大多数用户来说,切换到G1这样的低暂停收集器应该比并行GC这样的面向吞吐量的收集器(目前是默认的)提供更好的总体体验。

JDK8及其更新版本对G1进行了许多性能改进,并计划对JDK9进行进一步改进。JDK8u40中引入的并发类卸载(JEP156)使G1成为一个功能齐全的垃圾收集器,可以成为默认垃圾收集器。

GC调优:

最全面的JVM G1学习笔记_zl1zl2zl3的博客-CSDN博客

  1. 将gc日志打印出来。可以在虚拟机的启动gc参数中增加-XX:+PrintGCDetails参数,将每次的GC的耗时信息打印出来。
  1. 分析日志,找到到底是哪些GC阶段导致了GC时间的上涨。

%xwEx[GC pause (G1 Evacuation Pause) (young), 0.0962103 secs]

[Parallel Time: 23.3 ms, GC Workers: 4]

[GC Worker Start (ms): Min: 146441.2, Avg: 146441.3, Max: 146441.3, Diff: 0.1]

[Ext Root Scanning (ms): Min: 1.5, Avg: 1.8, Max: 2.4, Diff: 1.0, Sum: 7.2]

[Update RS (ms): Min: 1.0, Avg: 1.5, Max: 1.7, Diff: 0.6, Sum: 5.9]

[Processed Buffers: Min: 27, Avg: 34.8, Max: 41, Diff: 14, Sum: 139]

[Scan RS (ms): Min: 0.3, Avg: 0.3, Max: 0.3, Diff: 0.0, Sum: 1.3]

[Code Root Scanning (ms): Min: 0.0, Avg: 0.5, Max: 1.1, Diff: 1.1, Sum: 1.9]

[Object Copy (ms): Min: 18.3, Avg: 19.0, Max: 19.6, Diff: 1.2, Sum: 76.1]

[Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]

[Termination Attempts: Min: 1, Avg: 1.8, Max: 3, Diff: 2, Sum: 7]

[GC Worker Other (ms): Min: 0.0, Avg: 0.1, Max: 0.1, Diff: 0.1, Sum: 0.3]

[GC Worker Total (ms): Min: 23.1, Avg: 23.2, Max: 23.2, Diff: 0.1, Sum: 92.7]

[GC Worker End (ms): Min: 146464.4, Avg: 146464.4, Max: 146464.5, Diff: 0.1]

[Code Root Fixup: 0.1 ms]

[Code Root Purge: 0.0 ms]

[Clear CT: 0.7 ms]

[Other: 72.1 ms]

[Choose CSet: 0.0 ms]

[Ref Proc: 69.9 ms]

[Ref Enq: 0.6 ms]

[Redirty Cards: 0.1 ms]

[Humongous Register: 0.1 ms]

[Humongous Reclaim: 0.0 ms]

[Free CSet: 0.8 ms]

[Eden: 4824.0M(4824.0M)->0.0B(4820.0M) Survivors: 88.0M->92.0M Heap: 5044.1M(8192.0M)->224.1M(8192.0M)]

[Times: user=0.17 sys=0.00, real=0.09 secs]

1. 关键参数项

  • -XX:+UseG1GC,告诉

JVM使用G1垃圾收集器

  • -XX:MaxGCPauseMillis=200,设置GC暂停时间的目标最大值,这是个柔性的目标,JVM会尽力达到这个目标
  • -XX:INitiatingHeapOccupancyPercent=45,如果整个堆的使用率超过这个值,G1会触发一次并发周期。

记住这里针对的是整个堆空间的比例,而不是某个分代的比例。

2. 最佳实践

不要设置年轻代的大小

通过 -Xmn显式设置年轻代的大小,会干扰G1收集器的默认行为:

G1不再以设定的暂停时间为目标,换句话说,如果设置了年轻代的大小,就无法实现自适应的调整

来达到指定的暂停时间这个目标G1不能按需扩大或缩小年轻代的大小

响应时间度量

不要根据平均响应时间(ART)来设置 -XX:MaxGCPauseMillis=n这个参数,应该设置希望90%的GC都可以达到的暂停时间。

这意味着90%的用户请求不会超过这个响应时间,记住,这个值是一个目标,但是G1并不保证100%的GC暂停时间都可以达到这个目标

3. G1 GC的参数选项

参数名

含义

默认值

-XX:+UseG1GC

使用G1收集器

JDK1.8中还需要显式指定

-XX:MaxGCPauseMillis=n

设置一个期望的最大GC暂停时间,这是一个柔性的目标,JVM会尽力去达到这个目标

200

-XX:InitiatingHeapOccupancyPercent=n

当整个堆的空间使用百分比超过这个值时,就会触发一次并发收集周期,记住是整个堆

45

-XX:NewRatio=n

新生代和老年代的比例

2

-XX:SurvivorRatio=n

Eden空间和Survivor空间的比例

8

-XX:MaxTenuringThreshold=n

对象在新生代中经历的最多的新生代收集,或者说最大的岁数

G1中是15

-XX:ParallelGCThreads=n

设置垃圾收集器的并行阶段的垃圾收集线程数

不同的平台有不同的值

-XX:ConcGCThreads=n

设置垃圾收集器并发执行GC的线程数

n一般是ParallelGCThreads的四分之一

-XX:G1ReservePercent=n

设置作为空闲空间的预留内存百分比,以降低目标空间溢出(疏散失败)的风险。默认值是 10%。增加或减少这个值,请确保对总的 Java 堆调整相同的量

10

-XX:G1HeapRegionSize=n

分区的大小

堆内存大小的1/2000,单位是MB,值是2的幂,范围是1MB到32MB之间

-XX:G1HeapWastePercent=n

设置您愿意浪费的堆百分比。如果可回收百分比小于堆废物百分比,JavaHotSpotVM不会启动混合垃圾回收周期(注意,这个参数可以用于调整混合收集的频率)。

JDK1.8是5

-XX:G1MixedGCCountTarget=8

设置并发周期后需要执行多少次混合收集,如果混合收集中STW的时间过长,可以考虑增大这个参数。(注意:这个可以用来调整每次混合收集中回收掉老年代分区的多少,即调节混合收集的停顿时间)

8

-XX:G1MixedGCLiveThresholdPercent=n

一个分区是否会被放入mix GC的CSet的阈值。对于一个分区来说,它的存活对象率如果超过这个比例,则改分区不会被列入mixed gc的CSet中

JDK1.6和1.7是65,JDK1.8是85

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值