GC算法
-XX:+UseSerialGC
新生代和老年代都使用串行收集器
串行收集器使用单线程并且是独占式的垃圾回收
-XX:+UseParNewGC
新生代使用ParNew垃圾回收器,老年代使用串行收集器
ParNew是串行收集器的多线程版本,只工作在新生代(可以见名知义,ParNew:Paralle New,并行新生代)。可以考虑在CPU并发能力强的系统中使用ParNew,单CPU的话,性能不一定比串行收集器好。
-XX:+UseParallelGC
新生代使用ParallelGC回收器,老年代使用串行收集器
ParallelGC是工作在新生代的并行垃圾收集器,使用复制算法。只是它更关注系统的吞吐量,所以和ParNewGC的一个区别是有相关参数可以设置GC的停顿时间。
-XX:+UseParallelOldGC
新生代使用ParallelGC,老年代使用ParallelOldGC
ParallelOldGC是ParallelGC的老年代版本,只工作在老年代,使用标记压缩的回收算法,JDK1.6版本及之后支持
-XX:+UseConcMarkSweepGC
新生代使用ParNew,老年代使用CMS
CMS垃圾收集器更关注系统停顿时间,采用标记清除算法并且使用多线程并行回收。
CMS在GC失败时,老年代会使用串行收集器
-XX:+UseG1GC
使用G1回收器,同时作用于新生代和老年代
工作在新生代的垃圾收集器:SerialGC、ParNewGC、ParallelGC
工作在老年代的垃圾收集器:CMS、SerialGC、ParallelOldGC
G1同时在新生代和老年代工作。
查看当前jvm采用的GC算法
或查看gc日志
GC统计
1. cms简介
CMS(Concurrent Mark and Sweep 并发-标记-清除),是一款基于并发、使用标记清除算法的垃圾回收算法,只针对老年代进行垃圾回收。CMS收集器工作时,尽可能让GC线程和用户线程并发执行,以达到降低STW时间的目的。
-XX:+UseConcMarkSweepGC
值得补充的是,下面介绍到的CMS GC是指老年代的GC,而Full GC指的是整个堆的GC事件,包括新生代、老年代、元空间等,两者有所区分
1.1 名词解释
可达性分析算法:用于判断对象是否存活,基本思想是通过一系列称为“GC Root”的对象作为起点(常见的GC Root有系统类加载器、栈中的对象、处于激活状态的线程等),基于对象引用关系,从GC Roots开始向下搜索,所走过的路径称为引用链,当一个对象到GC Root没有任何引用链相连,证明对象不再存活。
Stop The World:GC过程中分析对象引用关系时,为了保证分析结果的准确性,需要通过停顿所有Java执行线程,保证引用关系不再动态变化,该停顿事件称为Stop The World(STW)。
Safepoint:代码执行过程中的一些特殊位置,当线程执行到这些位置的时候,说明虚拟机当前的状态是安全的,如果有需要GC,线程可以在这个位置暂停。HotSpot采用主动中断的方式,让执行线程在运行期轮询是否需要暂停的标志,若需要则中断挂起。
1.2 老年代垃圾回收
CMS GC以获取最小停顿时间为目的,尽可能减少STW时间,可以分为7个阶段
1.3 CMS常见问题
最终标记阶段停顿时间过长问题:
CMS的GC停顿时间约80%都在最终标记阶段(Final Remark),若该阶段停顿时间过长,常见原因是新生代对老年代的无效引用,在上一阶段的并发可取消预清理阶段中,执行阈值时间内未完成循环,来不及触发Young GC,清理这些无效引用
通过添加参数:-XX:+CMSScavengeBeforeRemark。在执行最终操作之前先触发Young GC,从而减少新生代对老年代的无效引用,降低最终标记阶段的停顿,但如果在上个阶段(并发可取消的预清理)已触发Young GC,也会重复触发Young GC
并发模式失败(concurrent mode failure) & 晋升失败(promotion failed)问题
并发模式失败:当CMS在执行回收时,新生代发生垃圾回收,同时老年代又没有足够的空间容纳晋升的对象时,CMS 垃圾回收就会退化成单线程的Full GC。所有的应用线程都会被暂停,老年代中所有的无效对象都被回收
晋升失败:当新生代发生垃圾回收,老年代有足够的空间可以容纳晋升的对象,但是由于空闲空间的碎片化,导致晋升失败,此时会触发单线程且带压缩动作的Full GC
内存碎片问题:
通常CMS的GC过程基于标记清除算法,不带压缩动作,导致越来越多的内存碎片需要压缩。
常见以下场景会触发内存碎片压缩:
- 新生代Young GC出现新生代晋升担保失败(promotion failed)
- 程序主动执行System.gc()
可通过参数CMSFullGCsBeforeCompaction的值,设置多少次Full GC触发一次压缩,默认值为0,代表每次进入Full GC都会触发压缩,带压缩动作的算法为上面提到的单线程Serial Old算法,暂停时间(STW)时间非常长,需要尽可能减少压缩时间。
模拟代码:
public static void main(String[] args) { List<byte[]> res = new ArrayList<>(); byte[] array = null; while (true){ int num=(int)(Math.random()*100-1); array = new byte[2 * 1024 * num]; res.add(array); System.out.println(array.length); } }
启动参数增加:参考:https://open.atatech.org/articles/249506
产生碎片可通过gc日志查看:
通过其中一段日志发现GC前:
剩余总空间 Total Free Space:82563*8/1024/1024=0.62MB,
最大连续空间 Max Chunk Size:230*8/1024=1.79KB
连续空间个数 Number of Blocks: 3349
碎片率 frag: 0.9683 = 96.83%
GC后:
剩余总空间 Total Free Space:84644*8/1024/1024=0.64MB,
最大连续空间 Max Chunk Size:254*8/1024=1.98KB
连续空间个数 Number of Blocks: 3356
碎片率 frag: 0.9943 = 99.43%
从第一次GC开始碎片率逐步升高,直到最后面的Number of Blocks等于1 ,碎片全部被清除,整个GC耗时2秒多。
GC统计数据变化:
发现到达一定时间后,老年代的GC次数在快速增加,GC耗时也在逐步增加, 直到最后的OutOfMemoryError
@cpe-172-100-1-50 ~ % jstat -gcutil 45198 5000
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 0.00 21.97 0.00 17.71 19.94 0 0.000 0 0.000 0.000
0.00 0.00 28.24 0.00 17.71 19.94 0 0.000 0 0.000 0.000
0.00 0.00 35.14 0.00 17.71 19.94 0 0.000 0 0.000 0.000
0.00 0.00 40.90 0.00 17.71 19.94 0 0.000 0 0.000 0.000
0.00 0.00 48.25 0.00 17.71 19.94 0 0.000 0 0.000 0.000
0.00 0.00 54.10 0.00 17.71 19.94 0 0.000 0 0.000 0.000
0.00 0.00 62.69 0.00 17.71 19.94 0 0.000 0 0.000 0.000
0.00 0.00 68.61 0.00 17.71 19.94 0 0.000 0 0.000 0.000
0.00 0.00 73.84 0.00 17.71 19.94 0 0.000 0 0.000 0.000
0.00 0.00 81.41 0.00 17.71 19.94 0 0.000 0 0.000 0.000
0.00 0.00 87.55 0.00 17.71 19.94 0 0.000 0 0.000 0.000
0.00 0.00 95.41 0.00 17.71 19.94 0 0.000 0 0.000 0.000
0.00 0.00 100.00 0.00 17.71 19.94 0 0.000 0 0.000 0.000
0.00 100.00 7.05 31.54 62.24 60.70 1 0.047 0 0.000 0.047
0.00 100.00 15.10 31.54 62.24 60.70 1 0.047 0 0.000 0.047
0.00 100.00 21.03 31.54 62.24 60.70 1 0.047 0 0.000 0.047
0.00 100.00 29.59 31.54 62.24 60.70 1 0.047 0 0.000 0.047
0.00 100.00 34.66 31.54 62.24 60.70 1 0.047 0 0.000 0.047
0.00 100.00 42.52 31.54 62.24 60.70 1 0.047 0 0.000 0.047
0.00 100.00 48.89 31.54 62.24 60.70 1 0.047 0 0.000 0.047
0.00 100.00 57.65 31.54 62.24 60.70 1 0.047 0 0.000 0.047
0.00 100.00 64.09 31.54 62.24 60.70 1 0.047 0 0.000 0.047
0.00 100.00 70.07 31.54 62.24 60.70 1 0.047 0 0.000 0.047
0.00 100.00 77.49 31.54 62.24 60.70 1 0.047 0 0.000 0.047
0.00 100.00 86.10 31.54 62.24 60.70 1 0.047 0 0.000 0.047
0.00 100.00 92.71 31.54 62.24 60.70 1 0.047 0 0.000 0.047
0.00 100.00 97.78 31.54 62.24 60.70 1 0.047 0 0.000 0.047
99.99 0.00 8.06 60.00 62.25 60.70 2 0.096 3 0.009 0.105
99.99 0.00 14.84 60.00 62.25 60.70 2 0.096 4 0.013 0.109
99.99 0.00 21.59 60.00 62.25 60.70 2 0.096 5 0.014 0.110
99.99 0.00 28.31 60.00 62.25 60.70 2 0.096 7 0.019 0.116
99.99 0.00 33.43 60.00 62.25 60.70 2 0.096 9 0.024 0.120
99.99 0.00 41.96 60.00 62.25 60.70 2 0.096 10 0.029 0.125
99.99 0.00 49.28 60.00 62.25 60.70 2 0.096 11 0.030 0.126
99.99 0.00 55.25 60.00 62.25 60.70 2 0.096 13 0.037 0.133
99.99 0.00 62.93 60.00 62.25 60.70 2 0.096 14 0.038 0.134
99.99 0.00 70.33 60.00 62.25 60.70 2 0.096 15 0.039 0.135
99.99 0.00 77.26 60.00 62.25 60.70 2 0.096 17 0.045 0.141
99.99 0.00 82.49 60.00 62.25 60.70 2 0.096 18 0.048 0.144
99.99 0.00 88.85 60.00 62.25 60.70 2 0.096 19 0.049 0.145
99.99 0.00 97.21 60.00 62.25 60.70 2 0.096 21 0.055 0.151
0.00 99.99 2.11 60.00 62.25 60.70 3 0.133 24 0.062 0.195
0.00 99.99 8.22 60.00 62.25 60.70 3 0.133 25 0.063 0.196
0.00 99.99 16.60 60.00 62.25 60.70 3 0.133 27 0.068 0.201
0.00 99.99 22.65 60.00 62.25 60.70 3 0.133 29 0.074 0.207
0.00 99.99 27.97 60.00 62.25 60.70 3 0.133 30 0.079 0.212
0.00 99.99 34.85 60.00 62.25 60.70 3 0.133 31 0.080 0.213
0.00 99.99 41.21 60.00 62.25 60.70 3 0.133 33 0.087 0.220
0.00 99.99 49.15 60.00 62.25 60.70 3 0.133 34 0.092 0.225
0.00 99.99 54.81 60.00 62.25 60.70 3 0.133 35 0.093 0.227
0.00 99.99 61.89 60.00 62.25 60.70 3 0.133 37 0.100 0.233
0.00 99.99 67.94 60.00 62.25 60.70 3 0.133 39 0.105 0.238
0.00 99.99 75.76 60.00 62.25 60.70 3 0.133 40 0.109 0.242
0.00 99.99 81.64 60.00 62.25 60.70 3 0.133 41 0.110 0.243
0.00 99.99 87.37 60.00 62.25 60.70 3 0.133 43 0.114 0.247
0.00 99.99 95.22 60.00 62.25 60.70 3 0.133 44 0.117 0.250
99.99 0.00 2.16 60.00 62.25 60.70 4 0.184 46 0.120 0.303
99.99 0.00 9.63 60.00 62.25 60.70 4 0.184 46 0.120 0.303
99.99 0.00 16.49 60.00 62.25 60.70 4 0.184 46 0.120 0.303
99.99 0.00 24.43 60.00 62.25 60.70 4 0.184 46 0.120 0.303
99.99 0.00 31.96 60.00 62.25 60.70 4 0.184 46 0.120 0.303
99.99 0.00 38.85 60.00 62.25 60.70 4 0.184 46 0.120 0.303
99.99 0.00 45.43 60.00 62.25 60.70 4 0.184 46 0.120 0.303
99.99 0.00 53.06 60.00 62.25 60.70 4 0.184 46 0.120 0.303
99.99 0.00 58.61 60.00 62.25 60.70 4 0.184 46 0.120 0.303
99.99 0.00 65.95 60.00 62.25 60.70 4 0.184 46 0.120 0.303
99.99 0.00 72.51 60.00 62.25 60.70 4 0.184 46 0.120 0.303
99.99 0.00 79.70 60.00 62.25 60.70 4 0.184 46 0.120 0.303
99.99 0.00 87.10 60.00 62.25 60.70 4 0.184 46 0.120 0.303
99.99 0.00 92.81 60.00 62.25 60.70 4 0.184 46 0.120 0.303
0.00 99.98 2.02 60.00 62.25 60.70 5 0.230 48 0.121 0.351
0.00 99.98 8.40 60.00 62.25 60.70 5 0.230 48 0.121 0.351
0.00 99.98 13.40 60.00 62.25 60.70 5 0.230 48 0.121 0.351
0.00 99.98 20.16 60.00 62.25 60.70 5 0.230 48 0.121 0.351
0.00 99.98 27.80 60.00 62.25 60.70 5 0.230 48 0.121 0.351
0.00 99.98 33.37 60.00 62.25 60.70 5 0.230 48 0.121 0.351
2. G1简介
G1(Garbage-First)是一款面向服务器的垃圾收集器,支持新生代和老年代空间的垃圾收集,主要针对配备多核处理器及大容量内存的机器,G1最主要的设计目标是: 实现可预期及可配置的STW停顿时间
2.1 G1堆空间划分
Region
为实现大内存空间的低停顿时间的回收,将划分为多个大小相等的Region。每个小堆区都可能是 Eden区,Survivor区或者Old区,但是在同一时刻只能属于某个代。
在逻辑上, 所有的Eden区和Survivor区合起来就是新生代,所有的Old区合起来就是老年代,且新生代和老年代各自的内存Region区域由G1自动控制,不断变动
巨型对象
当对象大小超过Region的一半,则认为是巨型对象(Humongous Object),直接被分配到老年代的巨型对象区(Humongous regions),这些巨型区域是一个连续的区域集,每一个Region中最多有一个巨型对象,巨型对象可以占多个Region。
G1把堆内存划分成一个个Region的意义在于:
每次GC不必都去处理整个堆空间,而是每次只处理一部分Region,实现大容量内存的GC
通过计算每个Region的回收价值,包括回收所需时间、可回收空间,在有限时间内尽可能回收更多的内存,把垃圾回收造成的停顿时间控制在预期配置的时间范围内,这也是G1名称的由来: garbage-first
2.2 G1工作模式
针对新生代和老年代,G1提供2种GC模式,Young GC和Mixed GC,两种会导致Stop The World
1、Young GC 当新生代的空间不足时,G1触发Young GC回收新生代空间 Young GC主要是对Eden区进行GC,它在Eden空间耗尽时触发,基于分代回收思想和复制算法,每次Young GC都会选定所有新生代的Region,同时计算下次Young GC所需的Eden区和Survivor区的空间,动态调整新生代所占Region个数来控制Young GC开销。
2、Mixed GC 当老年代空间达到阈值会触发Mixed GC,选定所有老年代里的Region,根据全局并发标记统计得出收集收益高的若干老年代 Region。在用户指定的开销目标范围内,尽可能选择收益高的老年代Region进行GC,通过选择哪些老年代Region和选择多少Region来控制Mixed GC开销。
2.3 G1注意点
Full GC问题
G1的正常处理流程中没有Full GC,只有在垃圾回收处理不过来(或者主动触发)时才会出现, G1的Full GC就是单线程执行的Serial old gc,会导致非常长的STW,是调优的重点,需要尽量避免Full GC,常见原因如下:
1) 程序主动执行System.gc()
2) 全局并发标记期间老年代空间被填满(并发模式失败)
3) Mixed GC期间老年代空间被填满(晋升失败)
4) Young GC时Survivor空间和老年代没有足够空间容纳存活对象
类似CMS,常见的解决是:
1) 增大-XX:ConcGCThreads=n 选项增加并发标记线程的数量,或者STW期间并行线程的数量:-XX:ParallelGCThreads=n
2) 减小-XX:InitiatingHeapOccupancyPercent 提前启动标记周期
3) 增大预留内存 -XX:G1ReservePercent=n ,默认值是10,代表使用10%的堆内存为预留内存,当Survivor区域没有足够空间容纳新晋升对象时会尝试使用预留内存
巨型对象分配
巨型对象区中的每个Region中包含一个巨型对象,剩余空间不再利用,导致空间碎片化,当G1没有合适空间分配巨型对象时,G1会启动串行Full GC来释放空间。可以通过增加 -XX:G1HeapRegionSize来增大Region大小,这样一来,相当一部分的巨型对象就不再是巨型对象了,而是采用普通的分配方式
不要设置Young区的大小
原因是为了尽量满足目标停顿时间,逻辑上的Young区会进行动态调整。如果设置了大小,则会覆盖掉并且会禁用掉对停顿时间的控制。
平均响应时间设置
使用应用的平均响应时间作为参考来设置MaxGCPauseMillis,JVM会尽量去满足该条件,可能是90%的请求或者更多的响应时间在这之内, 但是并不代表是所有的请求都能满足,平均响应时间设置过小会导致频繁GC。