在 上一篇 中,我们已经了解了 JVM 的各个分区及作用。本篇将介绍作用于 HeapSpace 和 MetaSpace 区域的 GC 。
GC 概览
从 JVM 诞生到现在,不管什么收集器,都是基于三种基本 GC 算法 的组合。收集器发展到目前,分成了两派,分代收集和分区收集,其目的都只有一个,提高 JVM 的吞吐量,降低或控制应用停顿时间。
垃圾识别
既然要执行 GC ,那 JVM 肯定要使用一定的方法,找出垃圾对象,如:
1. 引用计数
对每个对象的引用进行计数,每当有一个地方引用它时计数器 +1、引用失效则 -1,引用的计数放到对象头中,大于 0 的对象被认为是存活对象。
虽然 循环引用的问题可通过 Recycler 算法解决,但是在多线程环境下,引用计数变更也要进行昂贵的同步操作,性能较低,早期的编程语言会采用此算法。
2.可达性算法
从 GC Root 开始进行对象搜索,可以被搜索到的对象即为可达对象,此时还不足以判断对象是否存活/死亡,需要经过多次标记才能更加准确地确定,整个连通图之外的对象便可以作为垃圾被回收掉。目前 Java 中 主流的虚拟机均采用此算法。
那么什么对象 适合作 GC Root 呢?有以下四种:
- 程序运行时,虚拟机栈(为方法分配的帧栈中的局部变量表)中引用的对象
- 方法区(jdk7及之前)/ 元空间(jdk8及以后) 内静态属性、常量引用的对象
- 本地方法栈JNI(native方法)引用的对象
三种垃圾回收算法
-
Mark-Sweep(标记 - 清除):在 Heap 中,直接清除判定为垃圾的对象。该算法会带来内存碎片的问题,遇到大对象时,容易出现找不到连续可用内存空间的问题。
-
Copying(复制):GC 时将存活的对象,从一个区域复制到另外一个未使用的区。该算法将分配的内存再次分区,一定程度 上解决了内存碎片问题,但存在复制大对象时成本高的问题。
-
Mark-Compact(标记 - 整理):先通过 GC Root 标记存活对象,再对存活对象按照整理顺序(Compaction Order)进行整理。需要付出对象移动的代价
把 mark、sweep、compaction、copying 这几种动作的耗时放在一起看,大致有这样的关系:
compaction 耗时最长,因为该算法 可能要先计算一次对象的目标地址,然后修正指针,最后再移动对象,但其优点是不会产生内存碎片。是适用于 OldGen 的 GC。
用于 YoungGen 的 copy 算法:
-
当 Eden 区满或对象的内存分配不足时,触发 Minor GC,将存活对象复制一个 Survivor区。Survivor 区域进行GC 和存活对象的来回复制,并将复制的次数记录到对象头信息中。
-
部分对象会在两个 Survivor 区来回复制,复制一定次数后,晋升到老年代。复制的次数会在程序运行时动态调整。
- -XX:MaxTenuringThreshold=n: Sets the maximum tenuring threshold for use in adaptive GC sizing. The largest value is 15. The default value is 15 for the parallel (throughput) collector, and 6 for the CMS collector.
- ==-XX:InitialTenuringThreshold=n:==最少n次复制后,可以晋升到老年代。默认为7
为什么我们需要两个 Survivor 区?
复制算法中,使用两个 Survivor 区有两个目的,一是可以避免单个 Survivor 带来的内存碎片;二是为了对 TenuringThreshold 进行统计,准备晋升对象到 老年代。如果使用一个 Survivor 区,这两个目的实现更加复杂,具体细节可以自己想一下。
用于 OldGen 的 mark-compact 算法:
针对老年代对象存活率较高的对象回收,使用 标记-整理算法 ,将所有存活对象都向一端移动,然后直接清理掉端边界以外的内存
发生在老年代的GC称为Full GC,又称为Major GC,其经常会伴随至少一次的Minor GC(并非绝对,在Parallel Scavenge收集器中就有直接进行Full GC的策略选择过程)。Major GC的速度一般会比Minor GC慢10倍以上,故应尽量避免Full GC。
垃圾收集器
垃圾收集器是垃圾回收算法的具体实现,常见的 垃圾收集器 如下:
- Serial:串行收集,使用一个线程进行垃圾回收,会暂停 JVM 中执行的用户线程,无法对外提供服务
- Parallel:并行收集,使用多个线程进行垃圾回收,也会暂停 JVM 中执行的用户线程,对外暂停服务的时间变短
- CMS(Concurrent Mark Sweep):并发收集,用户线程与 GC 线程可同时运行,对外不会暂停服务
- G1(Garbage First):对堆内存分为不同的区域,并发的对每个区域进行垃圾回收,从 JDK 8 开始即 可以 使用,JDK 9 默认使用 这种方式。
如何查看当前 JVM 默认的的 GC?
在控制台使用如下命令查看 JVM 配置的参数:
>java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=132413952 -XX:MaxHeapSize=2118623232 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC (“+”表示并行收集被激活。)
java version "1.8.0_144"
Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)
如何查看某一个 jar 是否使用到了 UseG1GC ?
F:\IDEA_Project\springBoot\SpringBootDemo>jps #查看jvm中的进程
38224 Jps
39136 Launcher
28644
38984 Main1
F:\IDEA_Project\springBoot\SpringBootDemo>jinfo -flag UseG1GC 38984
-XX:+UseG1GC #“+”表示 Main1 使用到了 UseG1GC 收集器
F:\IDEA_Project\springBoot\SpringBootDemo>jinfo -flag UseParallelGC 38984
-XX:-UseParallelGC #“-”表示 Main1 没有使用 UseParallelGC 收集器
下图为 JDK 8 中的 GC 种类和适用的内存区域。8 中以分代 GC 为主,并引入了分区收集 G1 GC。
分代 收集器用例(环境:JDK 8)
用于测试的代码,相关 JVM 参数:
参数:
-Xms10m -Xmx10m -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+(垃圾收集器种类)
代码:
public static void main(String[] args) {
System.out.println("hi serial GC");
//1MB
int size=1*1024*1024;
byte[][] bytes=new byte[1024][];
for (int i = 0; i < 20; i++) {
bytes[i]=new byte[size];
}
}
下面对该段代码设置不同的垃圾收集器进行测试,观察 GC 日志。
Serial + SerialOld
Serial 收集器的特点是稳定,是一个单线程的垃圾收集收集器,会执行 STW(stop-the-word)暂停其他所有线程,等待垃圾收集结束。
我们在使用参数 -XX:+UseSerialGC时,即告知 JVM 在 Yong Generation 中使用 Serial 方式进行 GC(使用复制算法),在 Tenured Generation 中使用 Serial Old 方式进行 GC(使用标记整理算法)。
如下垃圾收集日志,便是使用的 UseSerialGC 。
-XX:InitialHeapSize=10485760 -XX:MaxHeapSize=10485760 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseSerialGC
[GC (Allocation Failure) [DefNew: 2752K->320K(3072K), 0.0023756 secs] 2752K->1179K(9920K), 0.0024418 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [DefNew: 3072K->319K(3072K), 0.0022960 secs] 3931K->1824K(9920K), 0.0023400 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
hi serial GC
[GC (Allocation Failure) [DefNew: 2624K->50K(3072K), 0.0023840 secs] 4129K->3920K(9920K), 0.0024102 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [DefNew: 2150K->49K(3072K), 0.0014084 secs] 6020K->5967K(9920K), 0.0014271 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [DefNew: 2141K->2141K(3072K), 0.0000138 secs][Tenured: 5918K->5918K(6848K), 0.0028538 secs] 8059K->8015K(9920K), [Metaspace: 3547K->3547K(1056768K)], 0.0029293 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [Tenured: 5918K->5887K(6848K), 0.0035693 secs] 8015K->7983K(9920K), [Metaspace: 3547K->3547K(1056768K)], 0.0036204 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
def new generation total 3072K, used 2253K [0x00000000ff600000, 0x00000000ff950000, 0x00000000ff950000)
eden space 2752K, 81% used [0x00000000ff600000, 0x00000000ff833790, 0x00000000ff8b0000)
from space 320K, 0% used [0x00000000ff8b0000, 0x00000000ff8b0000, 0x00000000ff900000)
to space 320K, 0% used [0x00000000ff900000, 0x00000000ff900000, 0x00000000ff950000)
tenured generation total 6848K, used 5887K [0x00000000ff950000, 0x0000000100000000, 0x0000000100000000)
the space 6848K, 85% used [0x00000000ff950000, 0x00000000fff0fed8, 0x00000000fff10000, 0x0000000100000000)
Metaspace used 3584K, capacity 4500K, committed 4864K, reserved 1056768K
class space used 389K, capacity 392K, committed 512K, reserved 1048576K
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at JVM.GarbageCollection.main(GarbageCollection.java:19)
ParNew + SerialOld
ParNew 是一个并行的多个线程的垃圾收集器,会执行 STW(stop-the-word)暂停其他所有线程,等待垃圾收集结束。
我们在使用参数 -XX:+UseParNewGC时,即告知 JVM 在 Yong Generation 中使用 Parallel 方式进行 GC(使用复制算法),在 Tenured Generation 中使用 Serial Old 方式进行 GC(使用标记整理算法)。
可用参数:
-XX:ParallelGCThreads=Num:配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。默认值为处理器数目。
如下垃圾收集日志,使用的便是 UseParNewGC 。
该方式与 UseSerialGC 的区别是,垃圾收集时的线程数不一致。值得注意的是日志末尾的提示,告知该方式的收集在 JDK 8 及以后被 Depracated 标记。
-XX:InitialHeapSize=10485760 -XX:MaxHeapSize=10485760 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParNewGC
[GC (Allocation Failure) [ParNew: 2752K->319K(3072K), 0.0009978 secs] 2752K->1204K(9920K), 0.0010476 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [ParNew: 3071K->320K(3072K), 0.0010187 secs] 3956K->1876K(9920K), 0.0010440 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
hi serial GC
[GC (Allocation Failure) [ParNew: 2564K->291K(3072K), 0.0016276 secs] 4120K->4214K(9920K), 0.0016511 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [ParNew: 2462K->75K(3072K), 0.0012649 secs] 6385K->6046K(9920K), 0.0012911 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [ParNew: 2180K->2180K(3072K), 0.0000129 secs][Tenured: 5970K->6656K(6848K), 0.0040956 secs] 8151K->7699K(9920K), [Metaspace: 3491K->3491K(1056768K)], 0.0041689 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[Full GC (Allocation Failure) [Tenured: 6656K->6656K(6848K), 0.0023813 secs] 8723K->8723K(9920K), [Metaspace: 3491K->3491K(1056768K)], 0.0024138 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [Tenured: 6656K->6560K(6848K), 0.0033044 secs] 8723K->8627K(9920K), [Metaspace: 3491K->3491K(1056768K)], 0.0033449 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
par new generation total 3072K, used 2226K [0x00000000ff600000, 0x00000000ff950000, 0x00000000ff950000)
eden space 2752K, 80% used [0x00000000ff600000, 0x00000000ff82cab0, 0x00000000ff8b0000)
from space 320K, 0% used [0x00000000ff8b0000, 0x00000000ff8b0000, 0x00000000ff900000)
to space 320K, 0% used [0x00000000ff900000, 0x00000000ff900000, 0x00000000ff950000)
tenured generation total 6848K, used 6560K [0x00000000ff950000, 0x0000000100000000, 0x0000000100000000)
the space 6848K, 95% used [0x00000000ff950000, 0x00000000fffb8390, 0x00000000fffb8400, 0x0000000100000000)
Metaspace used 3572K, capacity 4498K, committed 4864K, reserved 1056768K
class space used 387K, capacity 390K, committed 512K, reserved 1048576K
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at JVM.GarbageCollection.main(GarbageCollection.java:16)
Java HotSpot(TM) 64-Bit Server VM warning: Using the ParNew young collector with the Serial old collector is deprecated and will likely be removed in a future release
Parallel(Parallel Scanvenge) + Parallel Old :(JDK 8 默认使用的组合)
Parallel 是一个并行的多个线程的垃圾收集器,会执行 STW(stop-the-word)暂停其他所有线程,等待垃圾收集结束。
Parallel Scavenge 的目标是达到一个 可控的吞吐量,吞吐量=程序运行时间/(程序运行时间+GC时间)。Parallel Scavenge 提供了两个参数用以精确控制吞吐量,分别是用以控制最大 GC 停顿时间的 -XX:MaxGCPauseMillis 及直接控制吞吐量的参数 -XX:GCTimeRatio 。
我们在使用参数 -XX:+UseParallelGC 或 -XX:+UseParallelOldGC 时,即告知 JVM 在 Yong Generation 中使用 Parallel Scavenge 方式进行 GC(使用复制算法),在 Tenured Generation 中也使用 ParallelOld 方式进行 GC(使用标记整理算法)。
-XX:InitialHeapSize=10485760 -XX:MaxHeapSize=10485760 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
[GC (Allocation Failure) [PSYoungGen: 2048K->496K(2560K)] 2048K->971K(9728K), 0.0012240 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 2544K->480K(2560K)] 3019K->1595K(9728K), 0.0013929 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
hi serial GC
[GC (Allocation Failure) [PSYoungGen: 2120K->480K(2560K)] 8356K->7058K(9728K), 0.0009738 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Ergonomics) [PSYoungGen: 480K->0K(2560K)] [ParOldGen: 6578K->6881K(7168K)] 7058K->6881K(9728K), [Metaspace: 3542K->3542K(1056768K)], 0.0113635 secs] [Times: user=0.03 sys=0.00, real=0.01 secs]
[Full GC (Ergonomics) [PSYoungGen: 1088K->1024K(2560K)] [ParOldGen: 6881K->6427K(7168K)] 7970K->7451K(9728K), [Metaspace: 3543K->3543K(1056768K)], 0.0110307 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
[GC (Allocation Failure) --[PSYoungGen: 1024K->1024K(2560K)] 7451K->7451K(9728K), 0.0005160 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 1024K->1024K(2560K)] [ParOldGen: 6427K->6406K(7168K)] 7451K->7431K(9728K), [Metaspace: 3543K->3543K(1056768K)], 0.0104724 secs] [Times: user=0.03 sys=0.00, real=0.01 secs]
Heap
PSYoungGen total 2560K, used 1117K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)
eden space 2048K, 54% used [0x00000000ffd00000,0x00000000ffe174a8,0x00000000fff00000)
from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
to space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
ParOldGen total 7168K, used 6406K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)
object space 7168K, 89% used [0x00000000ff600000,0x00000000ffc41bd8,0x00000000ffd00000)
Metaspace used 3585K, capacity 4500K, committed 4864K, reserved 1056768K
class space used 389K, capacity 392K, committed 512K, reserved 1048576K
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at JVM.GarbageCollection.main(GarbageCollection.java:16)
ParNew + CMS(Concurrent Mark Sweep)
Concurrent Mark Sweep 并发标记清除,其目标是 低停顿、与用户线程并行执行 的垃圾收集器,适用于与用户交互的多的 B/S 服务器。
我们在使用参数 -XX:+UseConcMarkSweepGC 时,即告知 JVM 在 Yong Generation 中使用 Parallel New 方式进行 GC(使用复制算法),在 Tenured Generation 中使用 CMS 方式进行 GC(使用标记清除算法)。
CMS 执行分4步:
-
Initial Mark(初始标记):快速的标记 GC Roots 能 直接关联到的 对象, 需要STW
-
concurrent-mark(并发标记):进行 GC Roots 跟踪的过程,和用户线程一起工作。由前阶段标记过的对象出发,所有可到达的对象都在本阶段中标记。
-
concurrent-preclean(并发预清理):扫描 Card Table 脏的区域和新晋升到老年代的对象等,减少 Final Remark 的工作量。
-
concurrent-abortable-preclean(可中断的并发清理):和上一阶段的工作内容一致。
-
Final Remark(重新标记):在 Concurrent Mark 期间,因用户线程的运行,会使 Initial Mark 标记对象发生变动。该阶段对变动的那一部分对象重新标记,为正式的垃圾清理做最后的准备。Remark 阶段 需要 STW。
-
Concurrent Sweep(并发清除):清除 GC Roots 不可达对象,和用户线程一起工作,最后对死亡对象进行清除
-
concurrent-reset(并发重置):重置 CMS 相关数据结构
缺点:
8. 由于 CMS 产生的线程也需要占用堆内存,所以,CMS 必须要在老年代空间用尽前完成垃圾回收,否则 CMS 回收失败,将采用 Serial Old 方式执行 GC,由此带来更长的 STW。
9. 由于老年代采用的是 Mark-Sweep 算法,所以一定需要内存碎片整理。可以通过 -XX:CMSFullGCsBeforeCompaction=0 来指定间隔多少次进行一次内存碎片整理,0为默认值。
-XX:InitialHeapSize=10485760 -XX:MaxHeapSize=10485760 -XX:MaxNewSize=3497984 -XX:MaxTenuringThreshold=6 -XX:NewSize=3497984 -XX:OldPLABSize=16 -XX:OldSize=6987776 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:-UseLargePagesIndividualAllocation -XX:+UseParNewGC
[GC (Allocation Failure) [ParNew: 2748K->320K(3072K), 0.0029262 secs] 2748K->1194K(9920K), 0.0029924 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [ParNew: 3072K->320K(3072K), 0.0014933 secs] 3946K->1871K(9920K), 0.0015267 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
hi serial GC
[GC (Allocation Failure) [ParNew: 2668K->271K(3072K), 0.0019613 secs] 4220K->4138K(9920K), 0.0019933 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (CMS Initial Mark) [1 CMS-initial-mark: 3867K(6848K)] 5162K(9920K), 0.0001467 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-mark-start]
[GC (Allocation Failure) [ParNew: 2371K->118K(3072K), 0.0015342 secs] 6239K->6033K(9920K), 0.0015604 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [ParNew: 2209K->2209K(3072K), 0.0000173 secs][CMS[CMS-concurrent-mark: 0.001/0.003 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
(concurrent mode failure): 5915K->6616K(6848K), 0.0050711 secs] 8125K->7696K(9920K), [Metaspace: 3549K->3549K(1056768K)], 0.0051453 secs] [Times: user=0.03 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [CMS: 6616K->6400K(6848K), 0.0042853 secs] 8775K->8504K(9920K), [Metaspace: 3551K->3551K(1056768K)], 0.0043284 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [CMS: 6400K->6380K(6848K), 0.0043484 secs] 8504K->8483K(9920K), [Metaspace: 3551K->3551K(1056768K)], 0.0043880 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
初始标记,STW
[GC (CMS Initial Mark) [1 CMS-initial-mark: 6380K(6848K)] 8483K(9920K), 0.0001427 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
并发标记
[CMS-concurrent-mark-start]
[CMS-concurrent-mark: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-preclean-start]
[CMS-concurrent-preclean: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-abortable-preclean-start]
[CMS-concurrent-abortable-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
重新标记,STW
[GC (CMS Final Remark) [YG occupancy: 2177 K (3072 K)][Rescan (parallel) , 0.0001182 secs][weak refs processing, 0.0000062 secs][class unloading, 0.0002791 secs][scrub symbol table, 0.0004622 secs][scrub string table, 0.0001111 secs][1 CMS-remark: 6380K(6848K)] 8558K(9920K), 0.0010524 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
并发清除
[CMS-concurrent-sweep-start]
[CMS-concurrent-sweep: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-reset-start]
[CMS-concurrent-reset: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
par new generation total 3072K, used 2232K [0x00000000ff600000, 0x00000000ff950000, 0x00000000ff950000)
eden space 2752K, 81% used [0x00000000ff600000, 0x00000000ff82e320, 0x00000000ff8b0000)
from space 320K, 0% used [0x00000000ff8b0000, 0x00000000ff8b0000, 0x00000000ff900000)
to space 320K, 0% used [0x00000000ff900000, 0x00000000ff900000, 0x00000000ff950000)
concurrent mark-sweep generation total 6848K, used 6378K [0x00000000ff950000, 0x0000000100000000, 0x0000000100000000)
Metaspace used 3587K, capacity 4500K, committed 4864K, reserved 1056768K
class space used 389K, capacity 392K, committed 512K, reserved 1048576K
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at JVM.GarbageCollection.main(GarbageCollection.java:16)
小总结:
以上介绍的几种收集器,都是分代收集的思想,那么如何在不同的场景,选用不同的收集器呢?
- 追求大吞吐量,如后台数据清洗、定时作业任务等,使用 -XX:+UseParallelGC
- 需要快速响应,追求低停顿时间,如与用户交互相关的应用,使用 -XX:+UseConcMarkSweepGC
分区 收集器用例(环境:JDK 8)
G1(Garbage-First)(JDK 9 默认使用)
G1的垃圾回收是作用在 Region 和 Humongous 上的,所以我们先了解一下两个区域的相关的特性:
-
整体上 采用 标记-整理 算法,局部 是通过复制算法,没有内存碎片化问题。
-
G1 将堆空间分为多个大小一样的 Region(区域化),Region 的大小可以在启动设置(-XX:G1HeapRegionSize=32M) ,范围在1~32M,且必须是2的幂。默认的将整堆划分为2048个分区,也是最大数量,所以 G1 能够支持的最大内存为 64G。
-
Humongous区域,如果一个对象占用的空间超过了 Region 容量的 50%,那么他会被存放到 Humongous 区。如果一个 H 区装不下,那么 G1 就会寻找连续的H分区来存储。为了能找到连续的H区,有时候不得不启动 Full GC 。
-
G1 不再从物理区域上区分年轻代和老年代;但从逻辑上来讲,每个 Region 可以按需在年轻代和老年代之间切换,它们不再是物理隔离,而是一部分 Region 的集合且不需要 Region 是连续的。所以依然会采用不同 GC 方式来处理不同的区域。
-
STW 时间可控,添加了停顿预测机制,用户可以 指定最大停顿时间的期望值(-XX:MaxGCPauseMillis=m,JVM 将尽量保证停顿小于m)
-
将堆空间分成若干大小相同的子区域,GC 时按照区域扫描即可,缩小了扫描范围
G1是如何进行垃圾回收的?
4步过程:
- 初始标记(initial-mark):标记所有的 GC Root 。需要 STW。
- 并发标记(concurrent-mark-start):进行GC Roots Tracing的过程,可通过 ==-XX:ConcGcThreads=n ==指定并发 GC 使用的线程数
- 最终标记(Finalize Marking):修正并发标记期间,因程序运行导致标记发生变化的那一部分对象。需要 STW。
- 筛选回收(cleanup ):根据时间来进行价值最大化的回收,完成堆内存的整理。需要 STW。
-XX:InitialHeapSize=10485760 -XX:MaxHeapSize=10485760 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC -XX:-UseLargePagesIndividualAllocation
hi g1 GC
[GC pause (G1 Evacuation Pause) (young), 0.0025745 secs]
[Parallel Time: 1.7 ms, GC Workers: 4]
[GC Worker Start (ms): Min: 658.5, Avg: 658.5, Max: 658.5, Diff: 0.0]
[Ext Root Scanning (ms): Min: 0.0, Avg: 0.2, Max: 0.3, Diff: 0.3, Sum: 0.9]
[Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Processed Buffers: Min: 0, Avg: 0.0, Max: 0, Diff: 0, Sum: 0]
[Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Object Copy (ms): Min: 0.0, Avg: 0.9, Max: 1.3, Diff: 1.3, Sum: 3.7]
[Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.1, Diff: 0.1, Sum: 0.1]
[Termination Attempts: Min: 1, Avg: 1.8, Max: 3, Diff: 2, Sum: 7]
[GC Worker Other (ms): Min: 0.0, Avg: 0.4, Max: 1.6, Diff: 1.6, Sum: 1.8]
[GC Worker Total (ms): Min: 1.6, Avg: 1.6, Max: 1.7, Diff: 0.0, Sum: 6.6]
[GC Worker End (ms): Min: 660.1, Avg: 660.1, Max: 660.1, Diff: 0.0]
[Code Root Fixup: 0.0 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 0.1 ms]
[Other: 0.8 ms]
[Choose CSet: 0.0 ms]
[Ref Proc: 0.6 ms]
[Ref Enq: 0.0 ms]
[Redirty Cards: 0.1 ms]
[Humongous Register: 0.0 ms]
[Humongous Reclaim: 0.0 ms]
[Free CSet: 0.0 ms]
[Eden: 6144.0K(6144.0K)->0.0B(5120.0K) Survivors: 0.0B->1024.0K Heap: 6144.0K(10.0M)->1968.0K(10.0M)]
[Times: user=0.00 sys=0.00, real=0.00 secs]
1.初始标记
[GC pause (G1 Humongous Allocation) (young) (initial-mark) (to-space exhausted), 0.0064014 secs]
[Parallel Time: 4.7 ms, GC Workers: 4]
[GC Worker Start (ms): Min: 665.6, Avg: 665.6, Max: 665.6, Diff: 0.0]
[Ext Root Scanning (ms): Min: 1.2, Avg: 3.4, Max: 4.1, Diff: 2.9, Sum: 13.6]
[Update RS (ms): Min: 0.1, Avg: 0.2, Max: 0.4, Diff: 0.2, Sum: 0.9]
[Processed Buffers: Min: 3, Avg: 4.3, Max: 7, Diff: 4, Sum: 17]
[Scan RS (ms): Min: 0.1, Avg: 0.1, Max: 0.1, Diff: 0.0, Sum: 0.4]
[Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Object Copy (ms): Min: 0.1, Avg: 0.9, Max: 3.1, Diff: 3.0, Sum: 3.6]
[Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Termination Attempts: Min: 1, Avg: 2.3, Max: 5, Diff: 4, Sum: 9]
[GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[GC Worker Total (ms): Min: 4.6, Avg: 4.6, Max: 4.7, Diff: 0.0, Sum: 18.5]
[GC Worker End (ms): Min: 670.2, Avg: 670.2, Max: 670.2, Diff: 0.0]
[Code Root Fixup: 0.0 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 0.1 ms]
[Other: 1.6 ms]
[Evacuation Failure: 1.4 ms]
[Choose CSet: 0.0 ms]
[Ref Proc: 0.1 ms]
[Ref Enq: 0.0 ms]
[Redirty Cards: 0.1 ms]
[Humongous Register: 0.0 ms]
[Humongous Reclaim: 0.0 ms]
[Free CSet: 0.0 ms]
[Eden: 4096.0K(5120.0K)->0.0B(1024.0K) Survivors: 1024.0K->0.0B Heap: 8342.7K(10.0M)->6587.9K(10.0M)]
[Times: user=0.02 sys=0.00, real=0.01 secs]
[GC concurrent-root-region-scan-start]
[GC concurrent-root-region-scan-end, 0.0000328 secs]
2.并发标记
[GC concurrent-mark-start]
[GC concurrent-mark-end, 0.0019545 secs]
[GC pause (G1 Humongous Allocation) (young) (to-space exhausted), 0.0009880 secs]
[Parallel Time: 0.6 ms, GC Workers: 4]
[GC Worker Start (ms): Min: 674.8, Avg: 674.8, Max: 674.8, Diff: 0.0]
[Ext Root Scanning (ms): Min: 0.2, Avg: 0.2, Max: 0.2, Diff: 0.0, Sum: 0.7]
[Update RS (ms): Min: 0.3, Avg: 0.3, Max: 0.3, Diff: 0.0, Sum: 1.1]
[Processed Buffers: Min: 2, Avg: 3.0, Max: 4, Diff: 2, Sum: 12]
[Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Object Copy (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Termination Attempts: Min: 1, Avg: 1.0, Max: 1, Diff: 0, Sum: 4]
[GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[GC Worker Total (ms): Min: 0.5, Avg: 0.5, Max: 0.5, Diff: 0.0, Sum: 1.9]
[GC Worker End (ms): Min: 675.3, Avg: 675.3, Max: 675.3, Diff: 0.0]
[Code Root Fixup: 0.0 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 0.1 ms]
[Other: 0.3 ms]
[Evacuation Failure: 0.1 ms]
[Choose CSet: 0.0 ms]
[Ref Proc: 0.1 ms]
[Ref Enq: 0.0 ms]
[Redirty Cards: 0.1 ms]
[Humongous Register: 0.0 ms]
[Humongous Reclaim: 0.0 ms]
[Free CSet: 0.0 ms]
[Eden: 1024.0K(1024.0K)->0.0B(1024.0K) Survivors: 0.0B->0.0B Heap: 8075.6K(10.0M)->6607.7K(10.0M)]
[Times: user=0.00 sys=0.00, real=0.00 secs]
3.再次、最终标记
[GC remark [Finalize Marking, 0.0001166 secs] [GC ref-proc, 0.0002599 secs] [Unloading, 0.0005383 secs], 0.0010536 secs]
[Times: user=0.00 sys=0.00, real=0.00 secs]
4.筛选回收
[GC cleanup 8075K->8075K(10M), 0.0003422 secs]
[Times: user=0.00 sys=0.00, real=0.00 secs]
[GC pause (G1 Evacuation Pause) (young), 0.0006871 secs]
[Parallel Time: 0.4 ms, GC Workers: 4]
[GC Worker Start (ms): Min: 678.0, Avg: 678.0, Max: 678.0, Diff: 0.0]
[Ext Root Scanning (ms): Min: 0.2, Avg: 0.2, Max: 0.2, Diff: 0.0, Sum: 0.7]
[Update RS (ms): Min: 0.1, Avg: 0.1, Max: 0.2, Diff: 0.0, Sum: 0.6]
[Processed Buffers: Min: 2, Avg: 2.8, Max: 3, Diff: 1, Sum: 11]
[Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Object Copy (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1]
[Termination Attempts: Min: 1, Avg: 1.0, Max: 1, Diff: 0, Sum: 4]
[GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[GC Worker Total (ms): Min: 0.3, Avg: 0.3, Max: 0.4, Diff: 0.0, Sum: 1.4]
[GC Worker End (ms): Min: 678.4, Avg: 678.4, Max: 678.4, Diff: 0.0]
[Code Root Fixup: 0.0 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 0.1 ms]
[Other: 0.2 ms]
[Choose CSet: 0.0 ms]
[Ref Proc: 0.1 ms]
[Ref Enq: 0.0 ms]
[Redirty Cards: 0.1 ms]
[Humongous Register: 0.0 ms]
[Humongous Reclaim: 0.0 ms]
[Free CSet: 0.0 ms]
[Eden: 0.0B(1024.0K)->0.0B(1024.0K) Survivors: 0.0B->0.0B Heap: 8075.7K(10.0M)->7341.7K(10.0M)]
[Times: user=0.00 sys=0.00, real=0.00 secs]
[GC pause (G1 Humongous Allocation) (mixed) (to-space exhausted), 0.0006827 secs]
[Parallel Time: 0.3 ms, GC Workers: 4]
[GC Worker Start (ms): Min: 679.0, Avg: 679.0, Max: 679.0, Diff: 0.0]
[Ext Root Scanning (ms): Min: 0.2, Avg: 0.2, Max: 0.2, Diff: 0.0, Sum: 0.7]
[Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Processed Buffers: Min: 0, Avg: 0.5, Max: 2, Diff: 2, Sum: 2]
[Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Object Copy (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1]
[Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Termination Attempts: Min: 1, Avg: 1.3, Max: 2, Diff: 1, Sum: 5]
[GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[GC Worker Total (ms): Min: 0.2, Avg: 0.2, Max: 0.2, Diff: 0.0, Sum: 0.8]
[GC Worker End (ms): Min: 679.2, Avg: 679.2, Max: 679.2, Diff: 0.0]
[Code Root Fixup: 0.0 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 0.1 ms]
[Other: 0.3 ms]
[Evacuation Failure: 0.1 ms]
[Choose CSet: 0.0 ms]
[Ref Proc: 0.1 ms]
[Ref Enq: 0.0 ms]
[Redirty Cards: 0.1 ms]
[Humongous Register: 0.0 ms]
[Humongous Reclaim: 0.0 ms]
[Free CSet: 0.0 ms]
[Eden: 1024.0K(1024.0K)->0.0B(1024.0K) Survivors: 0.0B->0.0B Heap: 7382.2K(10.0M)->7341.7K(10.0M)]
[Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) 7341K->3533K(10M), 0.0047497 secs]
[Eden: 0.0B(1024.0K)->0.0B(3072.0K) Survivors: 0.0B->0.0B Heap: 7341.7K(10.0M)->3533.6K(10.0M)], [Metaspace: 3579K->3579K(1056768K)]
[Times: user=0.00 sys=0.00, real=0.01 secs]
相比于 CMS 的优势:
- Region 概念的提出,缩小了 GC 扫描范围,GC 的操作每个 Region 的时间变少,进而使得 GC pause 的时间变得可控。
- CMS 是分代收集中作用在 Tenured Generation 的收集器,G1 是分区收集中作用在每个 Region 的收集器
G1 从 JDK8 中开始引入;在 JDK12 及以后,添加了 GC 释放后的空闲内存返换给操作系统的功能。
就目前来讲,ZGC 和 Shenandoah 都还不稳定、完善,在此先了解一些基础概念,不做详细的介绍。
ZGC(The Z Garbage Collector)
适用于大内存 低延迟服务的内存管理和回收,SPECjbb 2015 基准测试,在 128G 的大堆下,最大停顿时间才 1.68 ms,停顿时间远胜于 G1 和 CMS。
设计目标包括:
-
停顿时间不超过10ms;
-
停顿时间不会随着堆的大小,或者活跃对象的大小而增加;
-
支持8MB~4TB级别的堆(未来支持16TB)
ZGC 在 JDK11 中推出 Experimental 版本,在 JDK13 中优化,添加了 GC 释放后的空闲内存返换给操作系统的功能。
Shenandoah(Red Hat研发)
对 G1 的升级,引入 brooks pointers 将 G1 中需要 STW 的过程去除了。对于这种低延迟的保证,是以消耗 CPU 资源为代价的。
引入的 JDK 版本:
下图为与 CMS 和 G1 等收集器的 Latency by percentile distribution(按百分比发布的延迟):
GC 日志解毒
不同的 GC 收集器中,打印的日志略有差异,我们看懂这些 GC Detail,才能判断内存使用有没有问题。
-XX:+UseParallelGC
[GC (Allocation Failure) [PSYoungGen: 2048K->496K(2560K)] 2048K->971K(9728K), 0.0012240 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Ergonomics) [PSYoungGen: 480K->0K(2560K)] [ParOldGen: 6578K->6881K(7168K)] 7058K->6881K(9728K), [Metaspace: 3542K->3542K(1056768K)], 0.0113635 secs] [Times: user=0.03 sys=0.00, real=0.01 secs]
-
GC 类型: GC 表示是一次YGC(Young GC)、Full GC 表示是一次老年代 GC
-
GC Cause:导致本次 GC 发生的原因,下面是几个常见的 cause
- Allocation Failure :内存分配失败
- Metadata GC Threshold: Metaspace 空间不足导致的 GC
- System.gc : 程序调用的 GC
- Ergonomics:负责自动调解gc暂停时间和吞吐量之间的平衡,提高虚拟机性能更好的一种做法。
- Promotion Failure: Old 区没有足够的空间分配给 Young 区晋升的对象(即使总可用内存足够大)。
- Concurrent Mode Failure: CMS GC 运行期间,Old 区预留的空间不足以分配给新的对象,此时收集器会发生退化,严重影响 GC 性能
- GCLocker Initiated GC: 如果线程执行在 JNI 临界区时,刚好需要进行 GC,此时 GC Locker 将会阻止 GC 的发生,同时阻止其他线程进入 JNI 临界区,直到最后一个线程退出临界区时触发一次 GC。
-
GC 的位置和空间大小变化:
- [PSYoungGen: 2048K->496K(2560K)] 2048K->971K(9728K), 0.0012240 secs],表示本次 GC 后 Young Gen 最大为 2560K,空间占用从 2048K 变为 496K;Heap 最大为 9728K,空间从 2048K 变为 971K,耗时 0.0012240 secs。
-
各阶段耗时统计:
- [Times: user=0.02 sys=0.01, real=0.00 secs] 分别表示,用户态占用时长,内核用时,真实用时。
-XX:+UseConcMarkSweepGC
[Full GC (Allocation Failure) [CMS: 6400K->6380K(6848K), 0.0043484 secs] 8504K->8483K(9920K), [Metaspace: 3551K->3551K(1056768K)], 0.0043880 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (CMS Initial Mark) [1 CMS-initial-mark: 6380K(6848K)] 8483K(9920K), 0.0001427 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-mark-start]
[CMS-concurrent-mark: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-preclean-start]
[CMS-concurrent-preclean: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-abortable-preclean-start]
[CMS-concurrent-abortable-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (CMS Final Remark) [YG occupancy: 2177 K (3072 K)][Rescan (parallel) , 0.0001182 secs][weak refs processing, 0.0000062 secs][class unloading, 0.0002791 secs][scrub symbol table, 0.0004622 secs][scrub string table, 0.0001111 secs][1 CMS-remark: 6380K(6848K)] 8558K(9920K), 0.0010524 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
[CMS-concurrent-sweep-start]
[CMS-concurrent-sweep: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-reset-start]
[CMS-concurrent-reset: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
- 第 2 行:开始初始标记(需 STW)
Old Gen 为共 6848K, 在使用到 6380K 时开始初始化标记,Heap 占用为 8483K,共 9920K,本次 CMS Initial Mark 耗时 0.0001427 secs。 - 第 3-4 行:并发标记阶段开始和耗时
目的:从GC Root 开始对堆中对象进行可达性分析,找出存活的对象。 - 第 5-6 行:并发预清理阶段开始和耗时
目的:查找在执行并发标记阶段新进入老年代的对象,减少下一个阶段 Final Remark 的工作 - 第 7-8 行:可中断的并发预清理阶段开始和耗时
- 第 9 行:最终标记(需STW)
目的:完成标记整个 OldGen 的所有的存活对象
本次CMS GC 后:YoungGen 占用 2177K,共 3072K;并发扫描耗费 0.0001182 secs;弱引用处理、垃圾类卸载、符号引用表擦洗、字符串引用表擦洗耗时;OldGen 占用 6380K,共 6848K, Heap 占用 8558K,共 9920K; - 第 10-11 行:并发整理阶段开始与耗时
- 第 12-13 行:并发重置阶段与耗时
目的:完成 CMS 相关数据结构的初始化,方便下一次 CMS 执行。
GC 常见问题
不同 GC 的触发条件
Young GC 触发条件
在 Eden 区为对象或 TLAB 分配内存失败,将导致一次 Young GC
CMS GC 触发的几个条件
CMS GC 在实现上分成 foreground collector 和 background collector。
1. foreground collector
CMS 收集器退化后的单线程串行 GC 模式,需要 STW 且时间长,需要格外注意。foreground collector 的 GC 算法有两种:
- 带压缩动作的算法,称为 MSC(Mark-Sweep-Compact),单线程全暂停的方式,对 Heap + Metaspace 进行垃圾收集,也就是真正意义上的 Full GC。
- 不带压缩动作的算法,收集 Old 区,暂停时间相对 MSC 算法短一些。
CMS 为了解决不带压缩动作算法产生的内存碎片问题,所以在每次进行 foreground collector 之前,会判断采用 MSC GC 还是 MS GC。
以上两种算法是在 JDK8 中讨论的,在 JDK 9 及以后的版本中,彻底去掉了 CMS forground collector 的功能,也就是说除了 background collector,就是压缩式的 Full GC。所以 UseCMSCompactAtFullCollection、CMSFullGCsBeforeCompaction 这两个参数也已经不再支持了。
2. background collector
background collector 由 CMS 后台线程间隔一定的时间(CMSWaitDuration=2000ms,默认2s,运行时可修改),去扫描,然后决定是否触发。
这其中,有5中情况会触发 background collector:
-
GC cause 是 _gc_locker 且开启了 GCLockerInvokesConcurrent (默认false);或者 GC cause 是_java_lang_system_gc(就是 System.gc()调用)且开启了 ExplicitGCInvokesConcurrent(默认false),那么将触发一次 background collector。
-
未开启 UseCMSInitiatingOccupancyOnly (默认false)情况下,根据统计数据动态计算,逻辑:
如果预测 CMS GC 完成所需要的时间大于预计的老年代将要填满的时间,则进行 background collector。但在 JVM 启动初期,没有历史数据,所以默认情况下,OldGen 占比到 50% 就进行了一次 background collector -
根据 OldGen 使用情况判断
a. Old Gen 空间使用占比情况与阈值比较,如果大于阈值则进行 CMS GC,由 CMSInitiatingOccupancyFraction 控制阈值,默认92%
b. 未开启 UseCMSInitiatingOccupancyOnly 情况下,Old Gen 进行了扩容,也会触发一次 background collector -
根据增量 GC 是否可能会失败(悲观策略):计算 Young GC 晋升是否安全
通过判断当前 Old Gen 剩余的空间大小是否足够容纳 Young GC 晋升的对象大小。 Young GC 到底要晋升多少是无法提前知道的,因此,这里通过统计平均每次 Young GC 晋升的大小和当前 Young GC 可能晋升的最大大小来进行比较 -
Metaspace 进行扩容时,触发一次 background collector
触发 Full GC 的几个场景
- OldGen 空间不足
- MetaSpace 空间不足
- 程序调用了 System.gc()
- 在要进行 young gc 的时候,根据之前统计数据发现 年轻代平均晋升大小 比现在 老年代剩余空间要大 ,那就会触发 full gc
- 待确定:堆中分配很大的对象
- 所谓大对象,是指需要大量连续内存空间的java对象,例如很长的数组,此种对象会直接进入老年代,而老年代虽然有很大的剩余空间,但是无法找到足够大的连续空间来分配给当前对象,此种情况就会触发JVM进行Full GC。