The method of submission of concurrency: Java GC tuning: Garbage collector

 Three-color algorithm, garbage collection efficiency, tuning JVM

 Garbage collector: garbage collector

 

 

What garbage?

     No one refers to it in

JVM GC collection algorithm:

  • Reference counting (ReferenceCounting)
    • Adding a reference to an object in the counter, whenever a reference to his place, the counter value is +1 when ,; references fail, the counter value is 1; any time counter for the object 0 is impossible to make the
    •  

       

       

       

    • Advantages: High efficiency determination

    • Disadvantages: not completely accurate, because if two objects refer to each other's problems appear to die

    •  

       

       

       Obviously, the last two examples are not to be used again (is equal to the null), but the GC can not recover, because the reference number is not zero, but 1, which resulted in a memory leak. It is also obvious, now the virtual machine is not in this manner.

       

  • Reachability analysis algorithm (root seaching)

           GC Roots through a series of objects as a starting point, the search starts from the root node down, called search path traversed reference chain (Reference Chain), when an object is not connected to any reference GC Roots chain, the prove that this object is not available.

Description:

(2.1), red for unreachable objects (recoverable objects)

(2.2), be careful! ! ! ! ! The figure does not mean that the whole area up method, part of the virtual machine stack up, native method stacks up not all, but merely to illustrate the three parts can be used as GC Roots!

3, can be used as an object GC Roots include the following

(3.1), the virtual machine stack (Local Variable Table stack frame) referenced object.

(3.2), the method area static property class object reference to an object or a constant reference.

(3.3), native method stacks the JNI object (that is native method) references.

Garbage collection algorithm (mark - sweep, copying, mark - finishing, generational collection)

1、标记 -清除算法(Mark-Sweep):内存碎片
“标记-清除”算法,如它的名字一样,算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。之所以说它是最基础的收集算法,是因为后续的收集算法都是基于这种思路并对其缺点进行改进而得到的。
    它的主要缺点有两个:
        (1)效率问题:标记和清除过程的效率都不高;
        (2)空间问题:标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致,碎片过多会导致大对象无法分配到足够的连续内存,从而不得不提前触发GC,甚至Stop The World。

                                       

 

2、复制算法(Copying)内存浪费
    为解决效率问题,“复制”收集算法出现了。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
    这样使得每次都是对其中的一块进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。
   它的主要缺点有两个:
        (1)效率问题:在对象存活率较高时,复制操作次数多,效率降低;
        (2)空间问题:內存缩小了一半;需要額外空间做分配担保(老年代)

    From Survivor, To Survivor使用的就是复制算法。老年代不使用这种算法,


                                         

 

3、标记-整理(Mark-Compact)-标记压缩:效率比copy略微底
    复制收集算法在对象存活率较高时就要执行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。    
    根据老年代的特点,有人提出了另外一种“标记-整理”(Mark-Compact)算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。


                                            

4、分代收集算法(Generational Collection)
    GC分代的基本假设:绝大部分对象的生命周期都非常短暂,存活时间短。
   “分代收集”算法,把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法(copy),只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清理”或“标记-整理”算法(mark comprt 或者sweep)来进行回收。

垃圾收集器搭配(很重要)



可以从老年代记起:SerialOld最厉害,可以搭配所有的新生代收集器;
ParallelOld只能搭配Parallel新生代的,也就是Parallel Scavenge;
CMS 只能搭配Serial和ParNew;
最后G1只能自己和自己玩。

服务器1.7,1.8默认parallel scavenge+Parallel old (卡顿stw 现象比较多):面试事可以说实战经验:将parallel scavenge+Parallel old替换成parallel scavenge+serial old或者parnew+serial +parnew+cms+serial old 
部分参考作者原文链接:https://blog.csdn.net/qq_19734597/article/details/80959567

Seiral 收集器

a stop-the-world ,copying collector which uses a single GC thresd.(STW

   特征:

  1. 是单线程的
  2. 在垃圾回收时,必须暂停其他所有线程的工作线程,即所谓的“Stop The World”
  3. jvm在Client模式下,默认的新生代收集器仍然是Serial收集器,虽然它有着上述两个重大的缺点,但也有这简单高效的优点。
  4. 单线程,没有线程交互开销
  5. 使用方法:-XX:+UseSerialGC
  6. 适用:运行在Client模式下的虚拟机。

Serial和Serial Old的协同工作模式

这里写图片描述

2 Serial Old收集器
特征

Serial Old是单线程,使用标记整理算法的Serial老年代版本,主要意义也是在于给Client模式下的虚拟机使用。

Server模式下用途:
在JDK1.5以及之前的版本中与Parallel Scavenge收集器搭配使用
当老年代使用CMS收集器出现故障时(Concurrent Mode Failure),可以作为CMS的后备选择。 
 

Serial和Serial Old的协同工作模式

 

这里写图片描述

 

 

3 ParNew 收集器
 

特征

其实就是Serial收集器的多线程版本,除了使用多条线程进行垃圾收集之外,其余的和Serial所有控制参数一样。
在配置为CMS 收集器的默认新生代收集器。
在多CPU的环境下可以发挥更高而效率,并且是唯一一个可以和CMS收集器搭配工作的新生代并行GC。
单CPU的环境下效率低于Serial
适用:运行在server模式下的虚拟机首选的新生代收集器。
使用方法:-XX:+UseParNewGC


 这里写图片描述

4 Parallel Scavenge(并行回收)收集器
特征

新生代收集器
使用复制算法,并行的多线程收集器
控制的吞吐量
吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)
停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验,而高吞吐量则可用高效率地利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。

参数:

-XX:MaxGCPauseMillis:控制最大垃圾收起停顿时间(毫秒)。

收集器会尽可能的保证每次垃圾收集耗费的时间不超过这个设定值。但是如果这个这个值设定的过小,那么Parallel Scavenge收集器为了保证每次垃圾收集的时间不超过这个限定值,会导致垃圾收集的次数增加和增加年轻代的空间大小,垃圾收集的吞吐量也会随之下降。

注意: XX:MaxGCPauseMillis设置的越小,吞吐量则必然越小。

-XX:GCTimeRatio: 设置吞吐量大小(>0,<100)。默认值为99,即最大允许1%的垃圾收集时间。

是应用程序运行时间和垃圾收集时间的比值。如果把值设置为19,即系统运行时间 : GC收集时间 = 19 : 1,那么GC收集时间就占用了总时间的5%【1 / (19 + 1) = 5%】,

-XX:+UseAdaptiveSizePolicy:

当这个参数打开之后,就不需要手工指定新生代的大小、Eden与Survivor区的比例、晋升老年代对象年龄等细节参数了,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量,这种调节方式称为GC自适应的调节策略(GC Ergonomics)。

只需要把基本的内存数据设置好(如-Xmx设置最大堆),然后使用MaxGVPauseMillis参数或GCTimeRation参数给虚拟机设立一个优化目标,JVM会自动调节其他优化参数.

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

Parallel Scavenge要和Parallel Old一起使用

Parallel Scavenge/Parallel Old运行示意图

 这里写图片描述

5 Parallel Old 收集器
特征

是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。
参数控制: -XX:+UseParallelOldGC 
只能和Parallel Scavenge配合使用,这个组合常用于注重吞吐量以及CPU资源敏感的场合。 
 

 

Parallel Scavenge/Parallel Old运行示意图

这里写图片描述

 

 

6 CMS(Concurrent Mark Sweep)收集器:卡顿次数与减少
特征

一种以获取最短回收停顿时间为目标的收集器。

适用于 :重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验的应用。

CMS收集器是基于“标记-清除”算法实现的,

运作过程分为4个步骤:

粗分:

1:初始标记(CMS initial mark)
需要“Stop The World”,仅仅只是标记一下GC Roots能直接关联到的对象,速度很快。
2.并发标记(CMS concurrent mark)

就是进行GC Roots Tracing(跟踪)的过程。(就是判断哪些对象不可达的过程)。

与用户线程一起工作。

3.重新标记(CMS remark)

需要“Stop The World”,是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。

4.并发清除(CMS concurrent sweep)

与用户线程一起工作。

 总体上来说,CMS收集器的内存回收过程是与用户线程一起并发地执行。 

 细分:

1、CMS initial mark:初始标记root,STW
2、CMS concurrent mark:并发标记
3、CMS-concurrent-preclean:并发预清理
4、CMS remark:重新标记,STW
5、CMS concurrent sweep:并发清除
6、CMS-concurrent-reset:并发重置
————————————————
jdk1.4开始引入,不是默认配置,内存32G,20个G在old区

 优点:并发收集‘,低停顿 低延时,老年代收集器
 缺点:CPU敏感,浮动垃圾(并发清理阶段),空间碎片(不能适应太大内存)



 缺点:
CMS收集器对CPU资源非常敏感。对于并发实现的收集器而言,虽然可以利用多核优势提高垃圾收集的效率,但是由于收集器在运行过程中会占用一部分的线程,这些线程会占用CPU资源,所以会影响到应用系统的运行,会导致系统总的吞吐量降低。CMS默认开始的回收线程数是(cpu数量 + 3) / 4,所以,当机器的CPU数量为4个以上的时候,垃圾回收线程将占用不少于%25的CPU资源,并且随着CPU数量的增加,垃圾回收线程占用的CPU资源会减少。但是,当CPU资源少于4个的时候,垃圾回收线程占用的CPU资源的比例会增大,会影响到系统的运行,假设有2个CPU的情况下,垃圾回收线程将会占据超过50%的CPU资源。所以,在选用CMS收集器的时候,需要考虑,当前的应用系统,是否对CPU资源敏感。

垃圾收集的过程中,无法处理浮动垃圾,所以可能会出现Concurrent Mode Failure问题而导致触发一次Full GC。浮动垃圾:是由于CMS收集器的并发清理阶段,清理线程是和用户线程一起运行,如果在清理过程中,用户线程产生了垃圾对象,由于过了标记阶段,所以这些垃圾对象就成为了浮动垃圾,CMS无法在当前垃圾收集过程中集中处理这些垃圾对象。由于这个原因,CMS收集器不能像其他收集器那样等到完全填满了老年代以后才进行垃圾收集,需要预留一部分空间来保证当出现浮动垃圾的时候可以有空间存放这些垃圾对象。在JDK 1.5中,默认当老年代使用了68%的时候会激活垃圾收集,这是一个保守的设置,如果在应用中老年代增长不是很快,可以通过参数 -XX:CMSInitiatingOccupancyFraction 控制触发的百分比,以便降低内存回收次数来提供性能。在JDK 1.6中,CMS收集器的激活阀值变成了92%。如果在CMS运行期间没有足够的内存来存放浮动垃圾,那么就会导致Concurrent Mode Failure失败,这个时候,虚拟机将启动后备预案,临时启动Serial Old收集器来对老年代重新进行垃圾收集,这样会导致垃圾收集的时间边长,特别是当老年代内存很大的时候。所以对参数-XX:CMSInitiatingOccupancyFraction的设置,过高,会导致发生Concurrent Mode Failure,过低,则浪费内存空间。

使用的"标记-清除"算法会出现很多内存碎片。过多的内存碎片会影响大对象的分配,会导致即使老年代内存还有很多空闲,但是由于过多的内存碎片,不得不提前触发垃圾Full GC。为了解决这个问题,CMS收集器提供了一个"-XX:+UseCMSCompactAtFullCollection"参数(默认是开启的),用于CMS收集器在必要的时候对内存碎片进行压缩整理。由于内存碎片整理过程不是并发的,所以会导致停顿时间变长。虚拟机还提供了一个-XX:CMSFullGCsBeforeCompaction"参数,来控制进行过多少次不压缩的Full GC以后,进行一次带压缩的Full GC,默认值是0,表示每次在进行Full GC前都进行碎片整理。

 参数
-XX:+UseConcMarkSweepGC:使用CMS收集器
 -XX:+ UseCMSCompactAtFullCollection: Full GC后,进行一次碎片整理;整理过程是独占的,会引起停顿时间变长。 
-XX:+CMSFullGCsBeforeCompaction  设置进行几次Full GC后,进行一次碎片整理。
-XX:ParallelCMSThreads  设定CMS的线程数量(一般情况约等于可用CPU数量) 。
CMS收集器示意图

这里写图片描述
 

7G1收集器(面向服务端)
 特点:

并行于并发:使用多个CPU(CPU或者CPU核心)来缩短stop-The-World停顿时间,其他需要停顿Java线程执行的GC动作,G1收集器仍然可以通过并发的方式让java程序继续执行。
分代收集:虽然G1可以不需要其他收集器配合就能独立管理整个GC堆,但是还是保留了分代的概念。它能够采用不同的方式去处理新创建的对象和已经存活了一段时间,熬过多次GC的旧对象以获取更好的收集效果。
空间整合:与CMS的“标记--清理”算法不同,G1从整体来看是基于“标记整理”算法实现的收集器;从局部上来看是基于“复制”算法实现的。但无论如何,都意味着G1运作期间不会产生内存空间碎片,收集后能够提供规整的可用内存,这种特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一次GC。
可预测的停顿:这是G1相对于CMS的另一大优势,降低停顿时间是G1和CMS共同关注点, 但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒。

 存储内存结构

 
G1会将堆划分为固定大小的多个区域,名称为Region,每一个Region都有对应一个remembered set(避免全堆扫描,记录每个对象是否可达),依然存在eden,S0,S1,old这些概念,不同的是是采用逻辑区分,而不是物理区分.每个heap区(Region)的大小在JVM启动时就确定了. JVM 通常生成 2000 个左右的heap区, 根据堆内存的总大小,一个Region的大小可以通过参数-XX:G1HeapRegionSize设定,范围允许为 1Mb 到 32Mb,且是2的指数倍。

 这里写图片描述

优先列表

 G1跟踪各个region里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region。这种使用Region划分内存空间以及有优先级的区域回收方式,保证了G1收集器在有限的时间内可以获取尽可能高的收集效率。
 

运行示意图

这里写图片描述

G1 Collector
G1垃圾收集器介绍:https://zhuanlan.zhihu.com/p/22591838

简介:
The first focus of G1 is to provide a solution for users running applications that require large heaps with limited GC latency.This means heap sizes of around 6GB or larger,and a stable and predictable pause time below 0.5 senconds.

G1属于新生代和老生代收集器
G1的几个概念
Region
SATB:Snapshot-At-The-Beginning,它是通过Root Tracing得到的,GC开始时候存活对象的快照。
RSet:记录了其他Region中的对象引用本Region中对象的关系,属于points-into结构(谁引用了我的对象)

YoungGC
新对象进入Eden区
存活对象拷贝到Survivor区
存活时间达到年龄阈值是,对象晋升到Old区

MixedGC
不是FullGC,回收所有的Young和部分Old
global concurrent marking

global concurrent marking
1、initial marking phase:标记GC Root,STW
2、Root region scanning phase:标记存活Region
3、Concurrent marking phase:标记存活的对象
4、Remark phase:重新标记,STW
5、Cleanup phase:部分STW

MixedGC时机
initiationHeapOccupancyPercent:堆占有率达到这个数值则触发global concurrent marking,默认45%
G1HeapWastePercent:在global concurrent marking结束之后,可以知道区有多少空间要被回收,在每次YGC之后和再次发生Mixed GC之前,会检查垃圾占比是否达到此参数,只有达到了,下次才会发生Mixed GC

MixedGC相关参数
G1MixedGCLiveThresholdPercent:Old 区的region被回收时候的存活对象占比
G1MixedGCCountTarget:
一次global concurrent marking之后,最多执行Mixed GC的次数
G1OldCSetRegionThresholdPercent:一次Mixed GC中能被选入CSet的最多old区的region数量

常用参数
-XX:+UseG1GC 开启G1
-XX:G1HeapRegionSize=n,region的大小,1~32M,2048个
-XX:MaxGCPauseMilles=200最大停顿时间
-XX:G1NewSizePercent,-XX:G1MaxNewSizePercent
-XX:G1ReservePercent=10, 保留防止to space溢出
-XX:ParallelGCThreads=n STW线程数
-XX:ConcGCThreads=n 并发线程数=1/4*并发

 

 

JVM参数:

   -标准参数,-x非标准参数,调优用-xx参数

查看虚拟机默认可设置的参数命令:Java -xx:PrintFlags -version

Java最小堆最大堆设同一个值,是为了防止抖动(拓展知识点)

GC TUNING 解决方式:

  1:系统上线前:预估预优化JVM的各种垃圾回收选择

  2:系统上线后:优化运行jvm运行环境解决jvm运行中出现的问题

  3:各时间段重启

  4系统垃圾回收机配置parallel scavenge+Parallel old替换成parallel scavenge+serial old或者parnew+serial +parnew+cms+serial old  or G1

※内存突高,定位问题: 

    命令:查看java进程 :

jps -mlVv
或
ps -ef| grep java

gc (GC堆状态) jstat -gc id  or jstat -gc id 1000 ->1000 毫秒刷新

jvisualvm  jdk1.8自带本地图形化监控工具 可参考:https://blog.csdn.net/u012550080/article/details/81605189

arthas 阿里开源Java诊断工具:https://alibaba.github.io/arthas/

jad+redefine:线上热编译

查找定位问题方法:

arthas thread :查看占内存的thread

  jmap -histo  ID |head -20 占内存的对象  https://www.cnblogs.com/atomicbomb/p/7646662.html

 

 

 

最佳实践:


年轻代大小
避免使用-Xmn、-XX:NewRatio等显示设置Young区大小,会覆盖停顿时间目标

暂停时间目标
暂停时间不要太苛刻,其吞吐量目标是90%的应用程序的时间和10%的垃圾回收时间,太苛刻会直接影响到吞吐量

关于MixedGC调优
-XX:InitiatingHeapOccupancyPercent
-XX:G1MixedGCLiveThresholdPercent、-XX:G1HeapWastePercent
-XX:G1MixedGCCountTarget
-XX:G1OldCSetRegionThresholdPercent
MixedGC调优推荐:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc_tuning.html#recommendations
G1垃圾收集器介绍:https://zhuanlan.zhihu.com/p/22591838

是否需要切换到G1
50%以上的堆被存活对象占用
对象分配和晋升的速度变化非常大
垃圾回收的时间特别长、超过了一秒

可视化gc日志分析工具
打印日志相关参数
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps
-XX:+PrintGCDateStamps
-Xloggc:$CATALINA_HOME/logs/gc.log :打印日志的位置
-XX:+PrintHeapAtGC
-XX:+PrintTenuringDistribution

cms日志
https://blogs.oracle.com/poonam/understanding-cms-gc-logs

g1日志
https://blogs.oracle.com/poonam/understanding-g1-gc-logs

在线工具:http://gceasy.io/
GCViewer
https://github.com/chewiebug/GCViewer
直接启动java -jar 启动就可以了
主要关注吞吐量和停顿时间这些值

Tomcat的gc调优实战
调优步骤:

打印gc日志
根据gc日志得到关键性指标
分析gc原因,调优jvm参数
初始化参数:
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps
-XX:+PrintGCDateStamps -XX:+DisableExplicitGC
-Xloggc:$CATALINA_HOME/logs/gc.log :打印日志的位置
-XX:+PrintHeapAtGC
-XX:+PrintTenuringDistribution
关于DisableExplicitGC这个,有必要了解一下,如果启动了这个参数,不能显示调用gc,System.gc()这个方法就没用了,那么在某一些使用到堆外内存的框架如netty中,就会存在内存泄漏的风险。
参考:https://blog.csdn.net/aitangyong/article/details/39403031
Parallel collector调优
parallel gc调优指南:

除非确定,否则不要设置最大堆内存
优先设置吞吐量目标
如果吞吐量目标达不到,调大最大内存,不能让os使用swap,如果仍然达不到,降低目标。
吞吐量能达到,GC时间太长、设置停顿时间的目标。
在官方文档的这个地方:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/ergonomics.html#sthref15
介绍了parallell gc调优的两种方式:
1、设置最大停顿时间XX:MaxGCPauseMillis
2、设置吞吐量XX:GCTimeRatio
这两个参数只适用于paraller collector
而在介绍Parallel collector的的文章这里:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/parallel.html#CHDCFBIF
说了另外两种方式:
3、设置内存动态变化:XX:YoungGenerationSizeIncrement、XX:TenuredGenerationSizeIncrement、XX:AdaptiveSizeDecrementScaleFactor,每次gc之后,都会看gc前后的内存,从而增加或者减少内存
4、通过查看gc的情况,设置内存大小,可以设置Xms(initial heap size) 、Xmx(maximum heap size)和Xmn(新生代的大小)还有MetaspaceSize和MaxMetaspaceSize
CMS GC调优
由于jdk1.8之后更加推荐使用G1,所以cms的调优自己看官网:
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/cms.html#concurrent_mark_sweep_cms_collector

G1 GC调优
g1gc调优指南:

年轻代大小
避免使用-Xmn、-XX:NewRatio等显示设置Young区大小,会覆盖停顿时间目标

暂停时间目标
暂停时间不要太苛刻,其吞吐量目标是90%的应用程序的时间和10%的垃圾回收时间,太苛刻会直接影响到吞吐量

关于MixedGC调优
-XX:InitiatingHeapOccupancyPercent
-XX:G1MixedGCLiveThresholdPercent、-XX:G1HeapWastePercent
-XX:G1MixedGCCountTarget
-XX:G1OldCSetRegionThresholdPercent
其实这里的介绍就是官网的介绍
官网:
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc_tuning.html#recommendations
这里介绍了调优的三种方式:
1、避免设置固定的内存例如Xms(initial heap size) 、Xmx(maximum heap size)和Xmn(新生代的大小)还有MetaspaceSize和MaxMetaspaceSize,但是可以必要条件下可以调这些参数
2、设置最大停顿时间例如;MaxGCPauseMillis=100
3、对于mixed gc参数的设置,如:
-XX:InitiatingHeapOccupancyPercent、
-XX:G1MixedGCLiveThresholdPercent、
-XX:G1HeapWastePercent、
-XX:G1MixedGCCountTarget 、
-XX:G1OldCSetRegionThresholdPercent

 

Guess you like

Origin www.cnblogs.com/ft-Pavilion/p/11993002.html