Java 虚拟机技术是每个 Java 开发工程师都应该深入掌握的。本系列文章将深入介绍 JVM 相关技术,主要包括内存划分、对象创建回收与分配以及垃圾收集三大部分。本系列文章将力求全面概要地汇总核心知识点,并使知识点串联成面,以方便学习、工作以及备忘复习。本文将介绍第三部分——垃圾收集。

垃圾收集算法

  • 标记清除算法
    • 需要标记的对象非常多,效率一般不高
    • 内存容易导致空间碎片化问题
  • 标记整理算法
    • 在标记清除算法基础上,增加内存整理功能,避免碎片化的问题
    • 同样存在标记量大而效率不高的问题
  • 标记复制算法
    • 一般需要预留 Survivor 区域,导致空间利用率不高
    • 回收效率非常高
  • 分代收集算法
    • 常见的垃圾收集器都采用此法

常见垃圾收集器

经典垃圾收集器

  • 新生代收集器

    • Serial New 收集器(-XX:+UseSerialGC)

      新生代单线程垃圾收集器。

    • Parallel 收集器(-XX:+UseParallelGC):

      也称为 Parallel Scavenge收集器,可以理解为 Serial New 收集器的多线程版本。该垃圾收集器可与 Serial Old 以及 Parallel Old 两款收集器搭配使用。

    • ParNew 收集器(-XX:+UseParNewGC)

      该垃圾收集器专门开发来与 CMS 收集器搭配使用,当然也可以与 Serial Old 垃圾收集器使用。

  • 老年代收集器

    • Serial Old 收集器(-XX:+UseSerialOldGC)
    • Parallel Old 收集器(-XX:+UseParallelOldGC)

      老年代并行垃圾收集器,仅与 Parallel 收集器搭配使用。

    • CMS 收集器(-XX:+UseConcMarkSweepGC)
  • 跨代收集器

    • G1 收集器(-XX:+UseG1GC)

注意,在 jdk1.8.0_161 中测试发现 -XX:+UseSerialOldGC 参数已经不可用。目前各个垃圾收集器之间的关系图如下:

其中标红线表示 jdk1.8.0_161 之后推荐使用的搭配关系,此外 CMS 在并发失败的时候还会退化为 Serial Old。 从 JDK 源码也可以看出端倪:

CMS 垃圾收集器

简介

CMS (Concurrent Mark Sweep)是老年代垃圾收集器,内部使用标记清除算法。其一般与 ParNew 搭配使用。CMS 的回收模式一般分为两种background 和 foreground 两种模式。background模式下,一般划分为以下几个阶段:

  • 初始标记:快速标记 GC Roots 直接引用的对象,该阶段为 Stop The World 过程;
  • 并发标记:从 GC Roots 直接引用对象出发遍历其他存活对象,该过程与用户线程并发运行,因此可能会有已被标记的对象状态发生变化。该过程持续时间会比较长,但不会 STW;
  • 重新标记:修正并发标记阶段状态发生变动的对象,该阶段停顿时间会比初始标记稍长,但远比并发标记要短暂。另外,CMS 主要采用了三色标记的增量更新算法实现重新标记;
  • 并发清理:清理未标记的对象,该阶段与用户线程并发运行。同时根据三色标记原理,该阶段新增对象都会被标记为黑色而不必清理。

另外一种模式是 foreground ,该模式下 CMS 将会退化为与 Serial Old 相同的收集算法,也就是采用单线程串行 GC 模式,STW 时间超长,有时会长达十几秒。这种模式下才能准确称为 Full GC。

CMS 存在几个缺点:

  • 对 CPU 资源敏感,会与应用程序争抢 CPU 资源;
  • 并发标记和并发清理阶段会产生浮动垃圾,需要等待下一次 GC 过程才能回收;
  • 标记清除算法会产生大量空间碎片(但可通过 -XX:+UseCMSCompactAtFullCollection 参数让 JVM 清理后进行内存整理);
  • 在并发标记和并发清理阶段,GC 线程与用户线程并发执行,此时如果再次触发 Major GC,会引起 “concurrent mode failure”,此时会进入 STW 状态,并退化为使用 Serial Old 垃圾收集器来回收。

常用调优参数:

参数参数含义默认值
-XX:+UseConcMarkSweepGC启用 CMS 收集器默认搭配 ParNew 新生代收集器
-XX:ConcGCThreads并发的 GC 线程数量默认值(CPU核心数+3)/ 4
-XX:+UseCMSCompactAtFullCollectionFull GC 后做压缩整理以减少内存碎片
-XX:CMSFullGCsBeforeCompaction指定多少次 Full GC 后压缩整理一次默认值是0,表示每次 Full GC 都会压缩一次
-XX:CMSInitiatingOccupancyFraction当老年代使用达到该比例会触发 Full GC默认值92(百分比)
-XX:+UseCMSInitiatingOccupancyOnly表示仅使用设定的回收阈值(这里指CMSInitiatingOccupancyFraction),如果不指定,JVM仅在第一次使用设定阈值,后续则自动调整,所以一般设置了回收阈值都会同时设置该值。
-XX:+CMSScavengeBeforeRemark在 Full GC 前执行一次 Minor GC,目的是减少老年代对年轻代的引用,从而降低标记阶段的开销
-XX:+CMSParallelInitialMarkEnabled表示在初始标记的时候多线程执行从而缩短 STW
-XX:+CMSParallelRemarkEnabled表示在重新标记的时候多线程执行从而缩短 STW默认开启

GC 日志案例:

运行参数: -Xms50m -Xmx50m -XX:+UseConcMarkSweepGC -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=128M -XX:+PrintGCDetails -XX:CMSInitiatingOccupancyFraction=92 -XX:+UseCMSInitiatingOccupancyOnly

## 初始标记[GC (CMS Initial Mark) [1 CMS-initial-mark: 30614K(34176K)] 34916K(49536K), 0.0015545 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] ## 并发标记[CMS-concurrent-mark-start][CMS-concurrent-mark: 0.014/0.014 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] [CMS-concurrent-preclean-start][CMS-concurrent-preclean: 0.000/0.000 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: 4302 K (15360 K)][Rescan (parallel) , 0.0008078 secs][weak refs processing, 0.0004334 secs][class unloading, 0.0038316 secs][scrub symbol table, 0.0038272 secs][scrub string table, 0.0003411 secs][1 CMS-remark: 30614K(34176K)] 34916K(49536K), 0.0093542 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] ## 并发清理[CMS-concurrent-sweep-start][CMS-concurrent-sweep: 0.003/0.003 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]

主要看点:

  • 初始标记耗时非常短(0.0015545 secs),触发阈值约是90%(30614/34176≈90%),这值比参数指定值92%略小,但在预期之内;
  • 并发标记阶段不会 STW,用时相对较长(0.014 secs);
  • 重新标记阶段默认就是多线程(Rescan (parallel)),也就是说 CMSParallelRemarkEnabled 默认开启;

常见调优思路

Java中9种常见的CMS GC问题分析与解决

G1 垃圾收集器

简介

G1 垃圾收集器与 CMS 收集器非常相似,但存在本质的区别。读者可以先看下一篇《Java 虚拟机原理 (四) G1垃圾收集器入门》进行了解,此处不再重复。

下边直接来看 G1 的垃圾回收阶段图:

具体各个阶段总结如下:

回收阶段是否 STW过程
年轻代收集 (young gc)是(收集过程中复制移动动作是一个 STW 过程)1.使用复制算法,从 eden 区将存活对象转移到 survivor区;2.如果对象超过了老化阈值,则会被晋升到老年代区块中。
初始标记(initial-mark)1.当老年代占堆比重达到 IHOP 阈值(缺省45%),则会触发并发标记周期,并进入初始标记阶段;2.初始标记过程是伴随着一次年轻代收集同时完成的;3.该过程主要是标记那些引用了老年代对象的 Survivor 区块(也就是根区块)
并发根区块扫描 (concurrent-root-scan)1.扫描那些引用了老年代的 Suvivor 区块(根区块)并标记他们所引用的对象。2.这个阶段是和应用线程并行执行。这个阶段必须在下一次 young gc 发生之前完成。
并发标记 (concurrent-mark)1.在全堆范围内寻找存活对象;2.该过程与应用线程并行执行;3.这个阶段可以被年轻代垃圾收集过程所打断(也就是中间可以穿插多个 young gc)
重新标记 (remark)1.最终完成堆中所有存活对象的标记;2.这个阶段使用了一个叫SATB(snapshot-at-the-beginning)的算法,该算法比CMS收集器所用算法快非常多。
并发清理 (cleanup)是(重置空区块阶段(3)为并发操作)1.清算存活对象以及空区块 (STW);2.擦除 RSets (STW);3.重置空区块,并添加进空闲区块列表;4.该阶段还会决定是否进入空间回收周期。
空间回收周期(space reclamation1.该过程会疏散或者复制存活对象那到新的未使用区块上;2.这过程可以包含多次 young gc,可以发生在年轻代区块中,GC日志中显示为[GC pause (young)],或者同时发生在年轻代和老年代区块中,而GC日志中显示为[GC Pause (mixed)];3.当G1判断不再值得回收老年代区块以获取更多空间的时候,便会结束该阶段,并进入下一个年轻代收集循环。

注意!当 G1 在回收存活对象或者是晋升对象到老年代的时候发生堆空间不足,将会触发晋升失败。此时,G1将进入 STW,并使用单线程来进行 Full GC。如果调优到位的话,你的应用应该避免Full GC发生。

综上,G1 垃圾收集模式分为三种:

  • Young GC:当 G1 评估当前年轻代的回收时间接近 -XX:MaxGCPauseMillis 指定值的时候,会触发 Young GC;
  • Mixed GC:当 老年代占有率达到 -XX:InitiatingHeapOccupancyPercent 指定值的时候,会触发并发周期。而并发清理阶段阶段会判断是否需要进入空间回收周期。在空间回收周期内,会根据期望的GC停顿时间以及区块的回收价值等来决定是否进行以及进行多少次 Mixed GC;
  • Full GC:STW,单线程收集过程。

常用调优参数:

参数参数含义默认值
-XX:+UseG1GC指定使用 G1 垃圾收集器
-XX:MaxGCPauseMillis=200设定一个最大的GC停顿时间目标。这是一个软目标,JVM会尽最大努力满足这个目标。但不宜设置过小,以免因为每次回收垃圾过少而导致频繁 GC。200 (ms)
-XX:GCPauseTimeInterval=GC 停顿间隔时间G1 在默认情况下不设置该值,而允许G1在一些极端的情况下连续执行垃圾回收
-XX:ParallelGCThreads=并行阶段最大的线程数根据CPU核心数N进行计算,如果N=8,那么ParallelGCThreads=N*5/8
-XX:ConcGCThreads=并发阶段最大的线程数默认值是-XX:ParallelGCThreads的值除以4
-XX:+G1UseAdaptiveIHOP-XX:InitiatingHeapOccupancyPercent=45配置启用IHOP,以及配置触发比例默认启用,触发比例为45%。注意,-XX:+G1UseAdaptiveIHOP这个选项会在JDK9里默认启用,即-XX:InitiatingHeapOccupancyPercent和XX:+GIUseAdaptivelHOP在JDK9之后只需要启用一个就可以了。JDK8环境下运行该选项会输出:“Unrecognized VM option ‘G1UseAdaptivelHOP’”
-XX:G1HeapRegionSize=设置区块大小整个堆最多划分为2048个Region,Region的大小一定是在1~32M之间,并且是2的N次方。 所以,如果堆的尺寸小于2G,那么Region数量就会少于2048。如果设置的堆大于64G,JVM会适当增加region的数量,但是region大小一定不会超过32M。
-XX:G1NewSizePercent=5-XX:G1MaxNewSizePercent=60配置年轻代占比的最小值和最大值最小值默认为5%,最大值默认为60%
-XX:G1HeapWastePercent=5允许浪费的堆内存比例。如果可回收百分比低于这个百分比,那么G1就不会触发Mixed GC。默认值5%
-XX:G1MixedGCCountTarget=8设置空间清理阶段的回收次数默认不超过8次
-XX:G1MixedGCLiveThresholdPercent=85如果老年代区块内部存活对象超过该比例,那么在空间清理阶段不会将该区块加入 CSet,也就是不会对其进行回收默认值85%
-XX:G1ReservePercent=10设置堆的保留空间阈值以降低发生晋升失败的可能。默认值是10
-XX:G1OldCSetRegionThresholdPercent=10设置Mixed GC期间要回收的老年代Region数量上限,即一次Mixed GC中最多可以被选入CSet中的老年代Region数量。默认值10%

GC 日志案例:

  • 案例一

    CommandLine flags: -XX:G1NewSizePercent=20 -XX:+HeapDumpOnOutOfMemoryError -XX:InitialHeapSize=3221225472 -XX:MaxGCPauseMillis=500 -XX:MaxHeapSize=5872025600 -XX:-OmitStackTraceInFastThrow -XX:+PrintGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UnlockExperimentalVMOptions -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC
    ## Young GC(以下同,略去详细内容)2021-01-08T04:12:09.331+0800: 1172604.919: [GC pause (G1 Evacuation Pause) (young), 0.0184242 secs]  [Parallel Time: 15.1 ms, GC Workers: 4]      [GC Worker Start (ms): Min: 1172604919.7, Avg: 1172604919.8, Max: 1172604919.8, Diff: 0.1]      [Ext Root Scanning (ms): Min: 1.4, Avg: 1.7, Max: 2.6, Diff: 1.2, Sum: 6.9]      [Update RS (ms): Min: 6.8, Avg: 7.4, Max: 7.8, Diff: 1.0, Sum: 29.8]        [Processed Buffers: Min: 11, Avg: 18.2, Max: 27, Diff: 16, Sum: 73]      [Scan RS (ms): Min: 0.4, Avg: 0.6, Max: 0.6, Diff: 0.2, Sum: 2.2]      [Code Root Scanning (ms): Min: 0.0, Avg: 0.1, Max: 0.1, Diff: 0.1, Sum: 0.3]      [Object Copy (ms): Min: 4.9, Avg: 5.0, Max: 5.1, Diff: 0.2, Sum: 19.9]      [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.1, Avg: 0.1, Max: 0.1, Diff: 0.1, Sum: 0.4]      [GC Worker Total (ms): Min: 14.8, Avg: 14.9, Max: 15.0, Diff: 0.2, Sum: 59.5]      [GC Worker End (ms): Min: 1172604934.6, Avg: 1172604934.6, Max: 1172604934.7, Diff: 0.1]  [Code Root Fixup: 0.0 ms]  [Code Root Purge: 0.0 ms]  [Clear CT: 0.5 ms]  [Other: 2.8 ms]      [Choose CSet: 0.0 ms]      [Ref Proc: 0.3 ms]      [Ref Enq: 0.0 ms]      [Redirty Cards: 0.1 ms]      [Humongous Register: 0.2 ms]      [Humongous Reclaim: 0.0 ms]      [Free CSet: 1.9 ms]  [Eden: 2498.0M(2498.0M)->0.0B(2496.0M) Survivors: 6144.0K->6144.0K Heap: 5011.5M(5576.0M)->2513.8M(5576.0M)][Times: user=0.06 sys=0.00, real=0.02 secs] ## 初始标记 initial-mark2021-01-08T04:12:18.958+0800: 1172614.546: [GC pause (G1 Evacuation Pause) (young) (initial-mark), 0.0199455 secs]  ### ......  [Eden: 2496.0M(2496.0M)->0.0B(2492.0M) Survivors: 6144.0K->8192.0K Heap: 5009.8M(5576.0M)->2515.0M(5576.0M)][Times: user=0.05 sys=0.02, real=0.02 secs] ## 并发根节点扫描2021-01-08T04:12:18.979+0800: 1172614.567: [GC concurrent-root-region-scan-start]2021-01-08T04:12:19.004+0800: 1172614.592: [GC concurrent-root-region-scan-end, 0.0256504 secs]## 并发标记2021-01-08T04:12:19.004+0800: 1172614.592: [GC concurrent-mark-start]2021-01-08T04:12:19.214+0800: 1172614.802: [GC concurrent-mark-end, 0.2101424 secs]## 重新标记2021-01-08T04:12:19.215+0800: 1172614.803: [GC remark 2021-01-08T04:12:19.215+0800: 1172614.803: [Finalize Marking, 0.0003026 secs] 2021-01-08T04:12:19.215+0800: 1172614.803: [GC ref-proc, 0.7285039 secs] 2021-01-08T04:12:19.944+0800: 1172615.532: [Unloading, 0.0301928 secs], 0.7619789 secs][Times: user=0.95 sys=0.02, real=0.77 secs] ## 清理阶段2021-01-08T04:12:19.978+0800: 1172615.566: [GC cleanup 2575M->2567M(5576M), 0.0076275 secs][Times: user=0.00 sys=0.00, real=0.01 secs] 2021-01-08T04:12:19.986+0800: 1172615.574: [GC concurrent-cleanup-start]2021-01-08T04:12:19.986+0800: 1172615.574: [GC concurrent-cleanup-end, 0.0000396 secs]## 空间收集阶段的 young gc2021-01-08T04:12:28.168+0800: 1172623.756: [GC pause (G1 Evacuation Pause) (young), 0.0249811 secs]   ### ......  [Eden: 2492.0M(2492.0M)->0.0B(1110.0M) Survivors: 8192.0K->4096.0K Heap: 4999.0M(5576.0M)->2504.2M(5576.0M)][Times: user=0.06 sys=0.03, real=0.02 secs] ## 空间收集阶段的 young gc(mixed)2021-01-08T04:12:31.149+0800: 1172626.737: [GC pause (G1 Evacuation Pause) (mixed), 0.0437074 secs]  ### ......  [Eden: 1110.0M(1110.0M)->0.0B(1108.0M) Survivors: 4096.0K->6144.0K Heap: 3614.2M(5576.0M)->2206.5M(5576.0M)][Times: user=0.10 sys=0.04, real=0.04 secs] ## 空间收集阶段的 young gc(mixed)2021-01-08T04:12:34.734+0800: 1172630.322: [GC pause (G1 Evacuation Pause) (mixed), 0.0397893 secs]  ### ......  [Eden: 1108.0M(1108.0M)->0.0B(1108.0M) Survivors: 6144.0K->6144.0K Heap: 3314.5M(5576.0M)->1916.7M(5576.0M)][Times: user=0.10 sys=0.04, real=0.04 secs] ## 空间收集阶段的 young gc(mixed)2021-01-08T04:12:39.836+0800: 1172635.424: [GC pause (G1 Evacuation Pause) (mixed), 0.0539989 secs]  ### ......  [Eden: 1108.0M(1108.0M)->0.0B(1110.0M) Survivors: 6144.0K->4096.0K Heap: 3024.7M(5576.0M)->1536.4M(5576.0M)][Times: user=0.13 sys=0.05, real=0.05 secs]
    • 案例二
    ## 大对象收集[GC pause (G1 Humongous Allocation) (young), 0.0022191 secs] [Parallel Time: 1.3 ms, GC Workers: 4]    [GC Worker Start (ms): Min: 909912.8, Avg: 909913.1, Max: 909913.9, Diff: 1.1]    [Ext Root Scanning (ms): Min: 0.0, Avg: 0.3, Max: 0.4, Diff: 0.4, Sum: 1.1]    [Update RS (ms): Min: 0.0, Avg: 0.1, Max: 0.3, Diff: 0.3, Sum: 0.4]       [Processed Buffers: Min: 0, Avg: 2.2, Max: 3, Diff: 3, Sum: 9]    [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.1, Diff: 0.1, Sum: 0.1]    [Termination (ms): Min: 0.0, Avg: 0.5, Max: 0.7, Diff: 0.7, Sum: 1.8]       [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.1]    [GC Worker Total (ms): Min: 0.0, Avg: 0.9, Max: 1.2, Diff: 1.2, Sum: 3.5]    [GC Worker End (ms): Min: 909914.0, Avg: 909914.0, Max: 909914.1, Diff: 0.1] [Code Root Fixup: 0.0 ms] [Code Root Purge: 0.0 ms] [Clear CT: 0.0 ms] [Other: 0.9 ms]    [Choose CSet: 0.0 ms]    [Ref Proc: 0.8 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(2048.0K)->0.0B(2048.0K) Survivors: 0.0B->0.0B Heap: 38.1M(50.0M)->38.1M(50.0M)][Times: user=0.00 sys=0.01, real=0.00 secs] ## Full GC[Full GC (Allocation Failure)  38M->37M(50M), 0.0419625 secs]  [Eden: 0.0B(2048.0K)->0.0B(2048.0K) Survivors: 0.0B->0.0B Heap: 38.1M(50.0M)->37.9M(50.0M)], [Metaspace: 30407K->30407K(1077248K)][Times: user=0.05 sys=0.00, real=0.04 secs] [Full GC (Allocation Failure)  37M->37M(50M), 0.0346627 secs]  [Eden: 0.0B(2048.0K)->0.0B(2048.0K) Survivors: 0.0B->0.0B Heap: 37.9M(50.0M)->37.9M(50.0M)], [Metaspace: 30407K->30407K(1077248K)][Times: user=0.05 sys=0.00, real=0.04 secs]

低延迟垃圾收集器

  • ZGC 收集器:JDK11 中推出的一款低延迟垃圾回收器,适用于大内存低延迟服务的内存管理和回收,SPECjbb 2015 基准测试,在 128G 的大堆下,最大停顿时间才 1.68 ms,停顿时间远胜于 G1 和 CMS。
  • Shenandoah 收集器:由 Red Hat 的一个团队负责开发,与 G1 类似,基于 Region 设计的垃圾收集器,但不需要 Remember Set 或者 Card Table 来记录跨 Region 引用,停顿时间和堆的大小没有任何关系。停顿时间与 ZGC 接近。

常用垃圾收集器公共参数

  • 以下两个命令结合起来可以大致推敲 JVM 默认参数:

    java -XX:+PrintFlagsInitial ${pid}  jinfo ${pid}
  • 以下列举常用的一些公共参数:

    参数参数含义默认值
    -Xms -Xmx -Xmn指定堆启动内存、堆最大内存、年轻代内存。比如 -Xmx5g 表示堆最大内存为5g。
    -XX:ErrorFile=file指定 Fatal Error日志文件位置,比如:./java_error_%p.log
    -XX:+HeapDumpOnOutOfMemoryError指示 JVM 发生 OutOfMemory 的时候输出 dump 文件
    -XX:-OmitStackTraceInFastThrow该参数禁用 OmitStackTraceInFastThrow。OmitStackTraceInFastThrow 默认开启,指示 hotspot 使用fast throw来优化这个抛出异常的地方,直接抛出一个事先分配好的、类型匹配的对象,这个对象的message和stack trace都被清空。所以开启OmitStackTraceInFastThrow不利于排查问题。
    -XX:+PrintGC指示输出简要GC日志
    -XX:+PrintGCDetails指示输出详细GC日志
    -XX:+PrintGCApplicationStoppedTime指示打印STW时间
    -XX:+PrintGCDateStamps指示输出GC的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
    -XX:+PrintGCTimeStamps该参数指示输出GC的时间戳(以JVM启动到当期的总时长的时间戳形式)
    -XX:+UseCompressedClassPointers该参数指示压缩kclass指针大小
    -XX:+UseCompressedOops该参数指示进行指针压缩(包含kclass指针和对象指针)
    -Xloggc:${GC_LOG_PATH}该参数指示输出GC日志到文件
    -XX:SurvivorRatio=8该参数设置的是Eden区与每一个Survivor区的比值,可以反推出占新生代的比值,Eden为8, 两个Survivor为1, Eden占新生代的4/5, 每个Survivor占1/10,两个占1/5默认8
    -XX:MetaspaceSize=64m-XX:MaxMetaspaceSize=128M该参数指定元数据空间初始扩容阈值为64m,最大扩容空间为128M默认值无穷大