参考文档:
java常用垃圾回收器G1和CMS的区别

jvm 针对新生代和年老代分别提供了多种不同的垃圾收集器,JDK1.6 中 Sun HotSpot 虚拟机的垃圾收集器如下:

image-20220318175835486

1. Client 模式和 Server 模式

参考:JVM的Client模式与Server模式

JVM 有两种运行模式 Server 与 Client。

两种模式的区别在于,Client 模式启动速度较快,Server 模式启动较慢;

但是启动进入稳定期长期运行之后 Server 模式的程序运行速度比 Client 要快很多。

这是因为 Server 模式启动的 JVM 采用的是重量级的虚拟机,对程序采用了更多的优化;而 Client 模式启动的 JVM 采用的是轻量级的虚拟机。

所以 Server 启动慢,但稳定后速度比 Client 远远要快。

使用 java -version 可以看到模式,默认 server。

2. Serial

单线程,复制算法。

Serial 是最基本垃圾收集器,使用复制算法,曾经是 JDK1.3.1 之前新生代唯一的垃圾收集器。Serial 是一个单线程的收集器,它不但只会使用一个 CPU 或一条线程去完成垃圾收集工作,并且在进行垃圾收集的同时,必须暂停其他所有的工作线程,直到垃圾收集结束

Serial 垃圾收集器虽然在收集垃圾过程中需要暂停所有其他的工作线程,但是它简单高效,对于限定单个 CPU 环境来说,没有线程交互的开销,可以获得最高的单线程垃圾收集效率,因此 Serial垃圾收集器依然是 java 虚拟机运行在 Client 模式下默认的新生代垃圾收集器。

3. ParNew

Serial+多线程

ParNew 垃圾收集器其实是 Serial 收集器的多线程版本,也使用复制算法,除了使用多线程进行垃圾收集之外,其余的行为和 Serial 收集器完全一样,ParNew 垃圾收集器在垃圾收集过程中同样也要暂停所有其他的工作线程

ParNew 收集器默认开启和 CPU 数目相同的线程数,可以通过-XX:ParallelGCThreads 参数来限制垃圾收集器的线程数。

ParNew 虽然是除了多线程外和 Serial 收集器几乎完全一样,但是 ParNew 垃圾收集器是很多 java 虚拟机运行在 Server 模式下新生代的默认垃圾收集器。

4. Parallel Scavenge

多线程复制算法,高效

Parallel Scavenge 收集器也是一个新生代垃圾收集器,同样使用复制算法,也是一个多线程的垃圾收集器,它重点关注的是程序达到一个可控制的吞吐量(Thoughput,CPU 用于运行用户代码的时间/CPU 总消耗时间,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)),高吞吐量可以最高效率地利用 CPU 时间,尽快地完成程序的运算任务,主要适用于在后台运算而不需要太多交互的任务。

自适应调节策略也是 ParallelScavenge 收集器与 ParNew 收集器的一个重要区别

5. Serial Old

单线程标记整理算法

Serial Old 是 Serial 垃圾收集器年老代版本,它同样是个单线程的收集器,使用标记-整理算法,这个收集器也主要是运行在 Client 默认的 java 虚拟机默认的年老代垃圾收集器

在 Server 模式下,主要有两个用途:

  1. 在 JDK1.5 之前版本中与新生代的 Parallel Scavenge 收集器搭配使用。
  2. 作为年老代中使用 CMS 收集器的后备垃圾收集方案。

新生代 Serial 与年老代 Serial Old 搭配垃圾收集过程图:

image-20220318181111259

新生代 Parallel Scavenge 收集器与 ParNew 收集器工作原理类似,都是多线程的收集器,都使用的是复制算法,在垃圾收集过程中都需要暂停所有的工作线程。

新生代 Parallel Scavenge/ParNew 与年老代 Serial Old 搭配垃圾收集过程图:

image-20220318181201757

6. Parallel Old

多线程标记整理算法

Parallel Old 收集器是 Parallel Scavenge 的年老代版本,使用多线程的标记-整理算法,在 JDK1.6 才开始提供。

在 JDK1.6 之前,新生代使用 Parallel Scavenge 收集器只能搭配年老代的 Serial Old 收集器,只能保证新生代的吞吐量优先,无法保证整体的吞吐量,Parallel Old 正是为了在年老代同样提供吞吐量优先的垃圾收集器,如果系统对吞吐量要求比较高,可以优先考虑新生代 Parallel Scavenge 和年老代 Parallel Old 收集器的搭配策略。

新生代 Parallel Scavenge 和年老代 Parallel Old 收集器搭配运行过程图:

image-20220318185420889

7. CMS

参考:

JVM第九周 JVM优化案例
java常用垃圾回收器G1和CMS的区别

多线程标记清除算法

Concurrent mark sweep(CMS)收集器是一种年老代垃圾收集器,其最主要目标是获取最短垃圾回收停顿时间,和其他年老代使用标记-整理算法不同,它使用多线程的标记-清除算法

最短的垃圾收集停顿时间可以为交互比较高的程序提高用户体验。

CMS 工作机制相比其他的垃圾收集器来说更复杂,整个过程分为以下 4 个阶段

初始标记

只是标记一下 GC Roots 能直接关联的对象,速度很快,仍然需要暂停所有的工作线程。

并发标记

进行 GC Roots 跟踪的过程,和用户线程一起工作,不需要暂停工作线程。

重新标记

为了修正在并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,仍然需要暂停所有的工作线程。

并发清除

清除 GC Roots 不可达对象,和用户线程一起工作,不需要暂停工作线程。由于耗时最长的并发标记和并发清除过程中,垃圾收集线程可以和用户现在一起并发工作,所以总体上来看 CMS 收集器的内存回收和用户线程是一起并发地执行。

CMS 收集器工作过程:

image-20220318185838509

8. G1

参考文档:

G1 收集器原理理解与分析
JVM调优参数大全及G1GC调优
java g1垃圾收集器优化参考

8.1 G1 概述

Garbage first 垃圾收集器是目前垃圾收集器理论发展的最前沿成果,相比与 CMS 收集器,G1 收集器两个最突出的改进是:

  1. 基于标记-整理算法,不产生内存碎片。
  2. 可以非常精确控制停顿时间,在不牺牲吞吐量前提下,实现低停顿垃圾回收。

G1 收集器避免全区域垃圾收集,它把堆内存划分为大小固定的几个独立区域,并且跟踪这些区域的垃圾收集进度,同时在后台维护一个优先级列表,每次根据所允许的收集时间,优先回收垃圾最多的区域。区域划分和优先级区域回收机制,确保 G1 收集器可以在有限时间获得最高的垃圾收集效率。

它满足高吞吐量的同时满足GC停顿的时间尽可能短。G1收集器专门针对以下应用场景设计

  • 可以像CMS收集器一样可以和应用并发运行
  • 压缩空闲的内存碎片,却不需要冗长的GC停顿
  • 对GC停顿可以做更好的预测
  • 不想牺牲大量的吞吐量性能
  • 不需要更大的Java Heap

G1从长期计划来看是以取代CMS为目标。与CMS相比有几个不同点使得G1成为GC的更好解决方案。

  1. G1会压缩空闲内存使之足够紧凑,做法是用regions代替细粒度的空闲列表进行分配,减少内存碎片的产生。
  2. G1的STW更可控,G1在停顿时间上添加了预测机制,用户可以指定期望停顿时间。

8.2 内存布局

在传统的GC收集器(serial,parallel,CMS)无一不例外都把heap分成固定大小连续的三个空间:young generation, old generation, and permanent generation

v2-a105b09b2dc65aeda369f01e064bae4a_720w

但G1却独辟蹊径,采用了一种全新的内存布局rO95LiK5qCR,size_20,color_FFFFFF,t_70,g_se,x_16

在G1中,有一种特殊的区域,叫Humongous区域。 如果一个对象占用的空间超过了分区容量50%以上,G1收集器就认为这是一个巨型对象。这些巨型对象,默认直接会被分配在年老代。

在G1中堆被分成一块块大小相等的heap region,一般有2000多块,这些region在逻辑上是连续的。每块region都会被打唯一的分代标志(eden,survivor,old)。在逻辑上,eden regions构成Eden空间,survivor regions构成Survivor空间,old regions构成了old 空间。

GC时G1的运行方式与CMS方式类似,会有一个全局并发标记(concurrent global marking phase)的过程,去确定堆里对象的的存活情况。并发标记完成之后,G1知道哪些regions空闲空间多(可回收对象多),优先回收这些空的regions,释放出大量的空闲空间。这是为什么这种垃圾回收方式叫G1的原因(Garbage-First)。

G1将其收集和压缩活动集中在堆中可能充满可回收对象(即垃圾)的区域,使用暂停预测模型来满足用户定义的暂停时间目标,并根据指定的暂停时间目标选择要收集的区域数量。

需要注意的是,G1不是实时收集器。它能够以较高的概率满足设定的暂停时间目标,但不是绝对确定的。根据以前收集的数据,G1估算出在用户指定的目标时间内可以收集多少个区域。因此,收集器对于收集区域的成本有一个相当准确的模型,它使用这个模型来确定在暂停时间目标内收集哪些区域和收集多少区域。

8.3 Region

G1中每个Region大小是固定相等的,Region的大小可以通过参数-XX:G1HeapRegionSize设定,取值范围从1M到32M,且是2的指数。如果不设定,那么G1会根据Heap大小自动决定。

决定逻辑:

size = (堆最小值+堆最大值)/ TARGET_REGION_NUMBER(2048) ,然后size取最靠近2的幂次数值, 并将size控制在[1M,32M]之间。

8.4 G1 垃圾回收方式

  1. Ygc:仅处理年轻代region
  2. MixedGc:包含所有年轻代以及部分老年代Region。
  3. FullGc:全堆扫描,每个Region

原则上G1会计算执行GC的时间,并且极力减少花在GC上的时间(包括ygc,mixgc),如果可能,会通过不断扩展堆空间来满足对象分配、转移的需要。

8.4.1 youngGC

触发:分配一般对象(非巨型对象)时,当所有 eden 的 region 使用达到最大阀值并且无法申请足够内存时。

younggc 会回收所有 Eden 以及 Survivor 区,并且将存活对象复制到 Old 区以及另一部分的 Survivor 区。

G1 控制 YGC 开销的手段是动态改变 young region 的个数,YGC 的过程中依然会 STW(stop the world 应用停顿),并采用多线程并发复制对象,减少 GC 停顿时间。

G1 不会回收整个堆,而是回收一个Collection Set(CS:回收区域集合)来进行回收,并且会估计整个Region的垃圾比例,优先回收垃圾占比高的Region。

但是这里必须考虑两个问题:

  1. 跨代引用(老年代对象持有年轻代引用)
  2. 不同Region之间互相引用

解决方法:
GC又将Region分成很多个卡片,并引入两个数据结构Card Table(用来记录卡片) 和 Remember Set(RS:被引用对象的Region用来记录引用对象的Card)。也就是说当两个Region有对象互相引用的时候,就会将引用对象的Card记录在另一个区域的RS里面,这样我们回收对象的时候,出现这种引用情况就不需要引用整个堆,而只需要扫描那个对应的Card就可以了,这是一个典型的“空换时”的概念。

在这里插入图片描述

年轻代垃圾回收只会回收 Eden 区和 Survivor 区。YGC 时,首先 G1 停止应用程序的执行(Stop-The-World),G1创建回收集(Collection Set),回收集是指需要被回收的内存分段的集合,年轻代回收过程的回收集包含年轻代Eden区和Survivor区所有的内存分段。

  1. 第一阶段,扫描根。根是指static变量指向的对象,正在执行的方法调用链条上的局部变量等。跟引用连同RSet记录的外部引用作为扫描存活对象的入口。
  2. 第二阶段,更新RSet。处理dirty card queue中的card,更新RSet。此阶段完成后,RSet可以准确的反映老年代对所在的内存分段中对象的引用。
  3. 第三阶段,处理RSet。识别被老年代对象指向的Eden中的对象,这些被指向的Eden中的对象被认为是存活的对象。
  4. 第四阶段,复制对象。此阶段,对象树被遍历,Eden区内存段中存活的对象会被复制到Survivor区中空的内存分段,Survivor区内存段中存活的对象如果年龄未达阈值,年龄会加1,达到阈值会被复制到Old区中空的内存分段。如果Survivor空间不够,Eden空间的部分数据会直接晋升到老年代空间。
  5. 第五阶段,处理引用。处理Soft,Weak,Phantom,Final,JNI Weak 等引用。最终Eden空间的数据为空,GC停止工作,而目标内存中的对象都是连续存储的,没有碎片,所以复制过程可以达到内存整理的效果,减少碎片。

我们考虑一个问题,ygc 是否需要扫描整个老年代

我们知道判断对象是否存活需要从 GC ROOTS 结点出发,从 GC ROOTS 结点可达的对象就是存活的。在 YGC 时,老年代中的对象是不回收的,也就意味着 GC ROOTS 里面应包含了老年代中的对象。但扫描整个老年代会很耗费时间,势必影响整个 GC 的性能!。所以在CMS 中使用了Card Table 的结构,里面记录了老年代对象到新生代引用。Card Table的结构是一个连续的byte[]数组,扫描Card Table的时间比扫描整个老年代的代价要小很多!

G1也参照了这个思路,不过采用了一种新的数据结构 Remembered Set 简称Rset。RSet 记录了其他 Region 中的对象引用本 Region 中对象的关系,属于points-into结构(谁引用了我的对象)。而 Card Table 则是一种 points-out(我引用了谁的对象)的结构,每个 Card 覆盖一定范围的 Heap(一般为512Bytes)。G1的 RSet 是在 Card Table 的基础上实现的:每个 Region 会记录下别的 Region 有指向自己的指针,并标记这些指针分别在哪些 Card 的范围内。 这个 RSet 其实是一个Hash Table,Key是别的Region的起始地址,Value是一个集合,里面的元素是Card Table的Index。每个Region都有一个对应的Rset

RSet究竟是怎么辅助GC的呢?在做YGC的时候,只需要选定young generation region的RSet作为根集,这些RSet记录了old->young的跨代引用,避免了扫描整个old generation。 而mixed gc的时候,old generation中记录了old->old的RSet,young->old的引用由扫描全部young generation region得到,这样也不用扫描全部old generation region。所以RSet的引入大大减少了GC的工作量。

所以G1中YGC不需要扫描整个老年代,只需要扫描Rset就可以知道老年代引用了哪些新生代中的对象。

8.4.2 MixGC

触发:一次 YoungGc 之后,老年代占据堆内存的百占比超过 InitiatingHeapOccupancyPercent(默认45%)时,超过这个值就会触发mixedGC。
混合回收都是基于复制算法进行的,把要回收的 Region 区存活的对象放入其他 Region,然后这个 Region 全部清理掉,这样就会不断空出来新的 Region;
有一个参数 -XX:G1HeapWastePercent,默认值5%,即空出来的区域大于整个堆的 5%,就会立即停止混合回收了。如正常默认回收次数是8次,但是可能到了4次,空闲Region大于整个堆的5%,就不会再进行后续回收了。

8.4.3 fullGC

G1 在对象复制/转移失败或者没法分配足够内存(比如巨型对象没有足够的连续分区分配)时,会触发 FullGC。

开始版本 FullGC 使用的是 stop the world 的单线程的 Serial Old 模式。JDK10 以后,Full GC 已经是并行运行,在很多场景下,其表现还略优于 Parallel GC 的并行 Full GC 实现。但是仍然要避免 fgc。

8.5 最佳实践

8.5.1 不要设置年轻代大小

通过 -Xmn 显式地指定了年轻代的大小, 会干扰到 G1收集器的默认行为:

  1. G1在垃圾收集时将不再关心暂停时间指标. 所以从本质上说,设置年轻代的大小将禁用暂停时间目标;
  2. G1在必要时也不能够增加或者缩小年轻代的空间. 因为大小是固定的,所以对更改大小无能为力;

8.5.2 暂停时间

其值不应该使用平均响应时间,应该考虑使用目标时间的90%或者更小作为响应时间指标. 即90%的用户(客户端)请求响应时间不会超过预设的目标值。

用-XX:MaxGCPauseMillis来指定,默认值200ms。这是一个软性目标,G1会尽量达成,如果达不成,会逐渐做自我调整。

对于Young GC来说,会逐渐减少Eden区个数,减少Eden空间那么Young GC的处理时间就会相应减少;

对于Mixed GC,G1会调整每次Choose Cset(要回收的 region 集合)的比例,默认最大值是10%,当然每次选择的Cset少了,所要经历的Mixed GC的次数会相应增加。同时减少Eden的总空间时,就会更加频繁的触发Young GC,也就是会加快Mixed GC的执行频率,因为Mixed GC是由Young GC触发的,或者说借机同时执行的。频繁GC会对对应用的吞吐量造成影响,每次Mixed GC回收时间太短,回收的垃圾量太少,可能最后GC的垃圾清理速度赶不上应用产生的速度,那么可能会造成串行的Full GC,这是要极力避免的。所以暂停时间肯定不是设置的越小越好。

当然也不能设置的偏大,转而指望G1自己会尽快的处理,这样可能会导致一次全部并发标记后触发的Mixed GC次数变少,但每次的时间变长,STW时间变长,对应用的影响更加明显。

8.5.3 转移失败

survivors 或 promoted objects 进行GC时如果JVM的heap区不足就会发生提升失败(promotion failure). 堆内存不能继续扩充,因为已经达到最大值了。当使用 -XX:+PrintGCDetails 时将会在GC日志中显示 to-space overflow (to-空间溢出)。该操作很昂贵,原因如下:

  1. GC仍继续所以空间必须被释放.
  2. 拷贝失败的对象必须被放到正确的位置(tenured in place).
  3. CSet指向区域中的任何 RSets 更新都必须重新生成(regenerated).

避免转移失败的方法:

  1. 增加保留内存大小, 其默认值是 10;G1保留内存大小,非必须不会使用保留内存;即增大-XX:G1ReservePercent=n
  2. 更早启动标记周期(marking cycle).即InitiatingHeapOccupancyPercent设置的小一点??
  3. 增加标记线程(marking threads)的数量. 合理设置-XX:ConcGCThreads=n

8.5.4 新生代优化

新生代优化-避免短生命对象进入老年代

预估每次Minor GC后存活下来对象的大小,合理的设置Survivor区,同时考虑高峰期间时,动态年龄判断条件的影响,不要让这种短生命周期对象侥幸逃脱进入老年代

8.5.5 region 大小

用-XX:G1HeapRegionSize来指定,若未指定则默认最多生成2048块,每块的大小需要为2的幂次方,如1,2,4,8,16,32,最大值为32M。Region的大小主要是关系到Humongous Object的判定,当一个对象超过Region大小的一半时,则为巨型对象,那么其会至少独占一个Region,如果一个放不下,会占用连续的多个Region。当一个Humongous Region放入了一个巨型对象,可能还有不少剩余空间,但是不能用于存放其他对象,这些空间就浪费了。所以如果应用里有很多大小差不多的巨型对象,可以适当调整Region的大小,尽量让他们以普通对象的形式分配,合理利用Region空间。

8.5.6 新生代比例

一般不需要设置新生代大小。让G1自己根据最大停顿时间动态调整。新生代比例有两个数值指定,下限:
-XX:G1NewSizePercent,默认值5%,上限:-XX:G1MaxNewSizePercent,默认值60%。G1会根据实际的GC情况(主要是暂停时间)来动态的调整新生代的大小,主要是Eden Region的个数。最好是Eden的空间大一点,毕竟Young GC的频率更大,大的Eden空间能够降低Young GC的发生次数。但是Mixed GC是伴随着Young GC一起的,如果暂停时间短,那么需要更加频繁的Young GC,同时也需要平衡好Mixed GC中新生代和老年代的Region,因为新生代的所有Region都会被回收,如果Eden很大,那么留给老年代回收空间就不多了,最后可能会导致Full GC。

8.5.7 并发 GC 线程数

通过 -XX:ConcGCThreads来指定,默认是-XX:ParallelGCThreads/4,也就是在非STW期间的GC工作线程数,当然其他的线程很多工作在应用上。当并发周期时间过长时,可以尝试调大GC工作线程数,但是这也意味着此期间应用所占的线程数减少,会对吞吐量有一定影响。

8.5.8 并行 GC 线程数

通过 -XX:ParallelGCThreads来指定,也就是在STW阶段工作的GC线程数,其值遵循以下原则:
① 如果用户显示指定了ParallelGCThreads,则使用用户指定的值。
② 否则,需要根据实际的CPU所能够支持的线程数来计算ParallelGCThreads的值,计算方法见步骤③和步骤④。
③ 如果物理CPU所能够支持线程数小于8,则ParallelGCThreads的值为CPU所支持的线程数。这里的阀值为8,是因为JVM中调用nof_parallel_worker_threads接口所传入的switch_pt的值均为8。
④ 如果物理CPU所能够支持线程数大于8,则ParallelGCThreads的值为8加上一个调整值,调整值的计算方式为:物理CPU所支持的线程数减去8所得值的5/8或者5/16,JVM会根据实际的情况来选择具体是乘以5/8还是5/16。
比如,在64线程的x86 CPU上,如果用户未指定ParallelGCThreads的值,则默认的计算方式为:ParallelGCThreads = 8 + (64 - 8) * (5/8) = 8 + 35 = 43。

8.5.9 被纳入Cset的Region的存活空间占比阈值

通过 -XX:G1MixedGCLiveThresholdPercent指定,不同版本默认值不同,有65%和85%。在全局并发标记阶段,如果一个Region的存活对象的空间占比低于此值,则会被纳入Cset。此值直接影响到Mixed GC选择回收的区域,当发现GC时间较长时,可以尝试调低此阈值,尽量优先选择回收垃圾占比高的Region,但此举也可能导致垃圾回收的不够彻底,最终触发Full GC。

8.5.10 触发全局并发标记的老年代使用占比

通过-XX:InitiatingHeapOccupancyPercent指定,默认值45%,也就是老年代占堆的比例超过45%。如果Mixed GC周期结束后老年代使用率还是超过45%,那么会再次触发全局并发标记过程,这样就会导致频繁的老年代GC,影响应用吞吐量。同时老年代空间不大,Mixed GC回收的空间肯定是偏少的。可以适当调高IHOP的值,当然如果此值太高,很容易导致年轻代晋升失败而出发Full GC,所以需要多次调整测试。

8.5.11 触发Mixed GC的堆垃圾占比

通过-XX:G1HeapWastePercent指定,默认值5%,也就是在全局标记结束后能够统计出所有Cset内可被回收的垃圾占整对的比例值,如果超过5%,那么就会触发之后的多轮Mixed GC,如果不超过,那么会在之后的某次Young GC中重新执行全局并发标记。可以尝试适当的调高此阈值,能够适当的降低Mixed GC的频率。

8.5.12 每轮Mixed GC回收的Region最大比例

通过-XX:G1OldCSetRegionThresholdPercent指定,默认10%,也就是每轮Mixed GC附加的Cset的Region不超过全部Region的10%,最多10%,如果暂停时间短,那么可能会少于10%。一般这个值不需要额外调整。

8.6 参数配置

8.6.1 G1 相关参数

-XX:MaxGCPauseMillis=200 - 设置最大GC停顿时间指标,JVM会尽力实现,但不保证. 默认值为200毫秒.
-XX:InitiatingHeapOccupancyPercent=45 - 如果老年代占据了堆内存的45%的时候,此时会触发一次mixGc。值为0则表示“一直执行GC循环)'. 默认值为45。
-XX:G1MixedGCLiveThresholdPercent:默认值是85%,确定要回收的Region的时候,必须是存活对象低于85%的Region才可以回收。
-XX:G1ReservePercent=n 设置堆内存保留为假天花板的总量,以降低提升失败的可能性. 默认值是 10.
-XX:ConcGCThreads=n 并发垃圾收集器使用的线程数量. 默认值随JVM运行的平台不同而不同.
-XX:+UseG1GC - 让 JVM使用G1垃圾收集器, jdk9被设为默认垃圾收集器;所以如果你的版本比较新则不再需要使用该参数
-XX:MetaspaceSize=256M 元空间,默认20M,确实有点小。
-XX:MaxMetaspaceSize=512M 最大元空间
 
下面参数不建议修改
-XX:G1NewSizePercent=5    设置年轻代占整个堆的最小百分比,默认值是堆的5%。需要开启-XX:UnlockExperimentalVMOptions
-XX:G1MaxNewSizePercent=60    设置年轻代占整个堆的最大百分比,默认值是堆的60%。
-XX:NewRatio=n 新生代与老生代(new/old generation)的大小比例(Ratio). 默认值为 2.
-XX:SurvivorRatio=n eden/survivor 空间大小的比例(Ratio). 默认值为 8.
-XX:MaxTenuringThreshold=n 年轻代提升到年老代的最大临界值. 默认值为 15.
-XX:G1HeapRegionSize=n region大小  默认值将根据 heap size 算出最优解;1M-32M
-XX:G1MixedGCCountTarget mixed回收执行次数,默认回收次数8。
-XX:G1HeapWastePercent,默认值是5%,就是说空出来的区域大于整个堆的5%,即使未达到回收次数,也会立即停止混合回收了。
如:默认回收次数是8次,但是可能到了4次,发现空闲Region大于整个堆的5%,就不会再进行后续回收了。
 
可参考官网说明:https://docs.oracle.com/en/java/javase/13/docs/specs/man/java.html
 
查看系统默认参数配置,如G1ReservePercent:  java -XX:+PrintFlagsFinal  | grep G1ReservePercent
 
JVM初始分配的堆内存由-Xms指定,默认是物理内存的1/64;
JVM最大分配的堆内存由-Xmx指定,默认是物理内存的1/4。
默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制。

8.6.2 日志记录参数

-XX:+HeapDumpAfterFullGC 在发生FGC时将当时的Java堆情况记录下来
-XX:+HeapDumpOnOutOfMemoryError  在发生 OOM 时将当时的Java堆情况记录下来
-Xloggc:filename 设置GC log文件的位置 -Xloggc:log/gc.log
-XX:+PrintGC 打印GC信息 默认关闭
-XX:+PrintGCDetails 打印GC详细信息 默认关闭
-XX:+PrintGCDateStamps GC 打印时间
-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=10
-XX:GCLogFileSize=50M