JVM调优主要就是调整下面两个指标

停顿时间:垃圾收集器做垃圾回收中断应用执行的时间。-XX:MaxGCPauseMillis

吞吐量:吞吐量是指应用程序线程用时占程序总用时的比例。 例如,吞吐量99/100意味着100秒的程序执行时间应用程序线程运行了99秒, 而在这一时间段内GC线程只运行了1秒。
注:垃圾收集的时间和总时间的占比:1/(1+n),吞吐量为1-1/(1+n)。__-XX:GCTimeRatio=n

一、GC调优步骤

1.打印GC日志

  1. -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:./gc.log

注:Tomcat可以直接加载JAVA_OPTS变量里
2.分析日志得到关键性指标
3.分析GC原因,调优JVM参数

二、Parallel Scavenge收集器(默认)

启动eureka服务的gc日志分析

  1. java -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:./gc-parallel.log -jar eureka-server-2.0.2.RELEASE.jar

分析gc-parallel.log

  1. Java HotSpot(TM) 64-Bit Server VM (25.102-b14) for windows-amd64 JRE (1.8.0_102-b14), built on Jun 22 2016 13:15:21 by "java_re" with MS VC++ 10.0 (VS2010)
  2. Memory: 4k page, physical 12493080k(6045448k free), swap 14983448k(6307336k free)
  3. CommandLine flags: -XX:InitialHeapSize=199889280 -XX:MaxHeapSize=3198228480 -XX:+PrintGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
  4. 2019-08-10T19:58:47.887+0800: 1.065: [GC (Allocation Failure) [PSYoungGen: 49152K->3312K(57344K)] 49152K->3320K(188416K), 0.1002477 secs] [Times: user=0.05 sys=0.00, real=0.10 secs]
  5. 2019-08-10T19:58:48.382+0800: 1.497: [GC (Allocation Failure) [PSYoungGen: 52464K->4048K(106496K)] 52472K->4064K(237568K), 0.0434123 secs] [Times: user=0.00 sys=0.02, real=0.04 secs]
  6. 2019-08-10T19:58:49.279+0800: 2.395: [GC (Allocation Failure) [PSYoungGen: 102352K->4912K(106496K)] 102368K->4936K(237568K), 0.0144670 secs] [Times: user=0.06 sys=0.00, real=0.01 secs]
  7. 2019-08-10T19:58:50.040+0800: 3.156: [GC (Metadata GC Threshold) [PSYoungGen: 79829K->6304K(204800K)] 79853K->6400K(335872K), 0.0166599 secs] [Times: user=0.06 sys=0.00, real=0.02 secs]
  8. 2019-08-10T19:58:50.057+0800: 3.172: [Full GC (Metadata GC Threshold) [PSYoungGen: 6304K->0K(204800K)] [ParOldGen: 96K->6261K(79872K)] 6400K->6261K(284672K), [Metaspace: 20516K->20516K(1067008K)], 0.0517235 secs] [Times: user=0.13 sys=0.02, real=0.05 secs]
  9. 2019-08-10T19:58:53.810+0800: 6.925: [GC (Allocation Failure) [PSYoungGen: 196608K->7653K(204800K)] 202869K->13922K(284672K), 0.0099539 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
  10. 2019-08-10T19:58:54.599+0800: 7.715: [GC (Allocation Failure) [PSYoungGen: 204261K->8703K(289792K)] 210530K->20812K(369664K), 0.0248190 secs] [Times: user=0.06 sys=0.00, real=0.03 secs]
  11. 2019-08-10T19:58:56.013+0800: 9.129: [GC (Allocation Failure) [PSYoungGen: 289791K->13293K(294400K)] 301900K->28482K(374272K), 0.0385212 secs] [Times: user=0.16 sys=0.01, real=0.04 secs]
  12. 2019-08-10T19:58:57.993+0800: 11.109: [GC (Allocation Failure) [PSYoungGen: 294381K->17389K(448512K)] 309570K->34338K(528384K), 0.0282796 secs] [Times: user=0.08 sys=0.00, real=0.03 secs]
  13. 2019-08-10T19:58:59.202+0800: 12.318: [GC (Metadata GC Threshold) [PSYoungGen: 59097K->17933K(452096K)] 76046K->34890K(531968K), 0.0202485 secs] [Times: user=0.05 sys=0.00, real=0.02 secs]
  14. 2019-08-10T19:58:59.223+0800: 12.338: [Full GC (Metadata GC Threshold) [PSYoungGen: 17933K->0K(452096K)] [ParOldGen: 16957K->33158K(147968K)] 34890K->33158K(600064K), [Metaspace: 33942K->33942K(1079296K)], 0.1874046 secs] [Times: user=0.49 sys=0.02, real=0.19 secs]
  15. 2019-08-10T19:59:06.796+0800: 19.911: [GC (Allocation Failure) [PSYoungGen: 431104K->6320K(549376K)] 464262K->39486K(697344K), 0.0150792 secs] [Times: user=0.06 sys=0.00, real=0.02 secs]
  16. 2019-08-10T19:59:11.410+0800: 24.526: [GC (Allocation Failure) [PSYoungGen: 549040K->13048K(573952K)] 582206K->46222K(721920K), 0.0296726 secs] [Times: user=0.09 sys=0.03, real=0.03 secs]
  17. 2019-08-10T20:09:14.424+0800: 627.539: [GC (Allocation Failure) [PSYoungGen: 564472K->14719K(675328K)] 597646K->47902K(823296K), 0.0259811 secs] [Times: user=0.11 sys=0.02, real=0.03 secs]

使用GCeasy可视化分析日志:
调优前
image.png
详细结果请看如下链接
https://www.gceasy.io/diamondgc-report.jsp?oTxnId_value=542a04a7-8772-4aa9-aa92-754ce195d4b4
第一次调优,设置Metaspace大小:增大元空间大小-XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=64M
image.pnghttps://www.gceasy.io/diamondgc-report.jsp?oTxnId_value=76139c9e-f3c3-446d-b1b9-9f3156c6eeb6
第二次调优,增大年轻代动态扩容增量(默认是20%),可以减少YGC:-XX:YoungGenerationSizeIncrement=30
image.png
https://www.gceasy.io/diamondgc-report.jsp?oTxnId_value=76218376-20f7-4778-95a9-69593a02cd5a
比较下几次调优效果:

吞吐量 最大停顿 平均停顿 YGC FGC
99.901% 190ms 44.3ms 14 2 调优前
99.53% 70ms 23.3ms 12 0 第一次调优
98.669% 80ms 26.4ms 11 0 第二次调优

三、配置CMS收集器


-XX:+UseConcMarkSweepGC

分析gc-cms.log

四、配置G1收集器


-XX:+UseG1GC
启动eureka服务的gc日志分析

  1. java -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+UseG1GC -Xloggc:./gc-g1.log -jar eureka-server-2.0.2.RELEASE.jar

分析gc-g1.log

  1. Java HotSpot(TM) 64-Bit Server VM (25.102-b14) for windows-amd64 JRE (1.8.0_102-b14), built on Jun 22 2016 13:15:21 by "java_re" with MS VC++ 10.0 (VS2010)
  2. Memory: 4k page, physical 12493080k(6729568k free), swap 14983448k(6356832k free)
  3. CommandLine flags: -XX:InitialHeapSize=199889280 -XX:MaxHeapSize=3198228480 -XX:+PrintGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC -XX:-UseLargePagesIndividualAllocation
  4. 2019-08-10T20:47:49.791+0800: 0.647: [GC pause (G1 Evacuation Pause) (young), 0.1467107 secs]
  5. [Parallel Time: 145.5 ms, GC Workers: 4]
  6. [GC Worker Start (ms): Min: 646.7, Avg: 646.7, Max: 646.7, Diff: 0.0]
  7. [Ext Root Scanning (ms): Min: 0.2, Avg: 15.5, Max: 61.1, Diff: 60.9, Sum: 62.0]
  8. [Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
  9. [Processed Buffers: Min: 0, Avg: 0.0, Max: 0, Diff: 0, Sum: 0]
  10. [Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
  11. [Code Root Scanning (ms): Min: 0.0, Avg: 15.2, Max: 60.4, Diff: 60.4, Sum: 60.7]
  12. [Object Copy (ms): Min: 84.2, Avg: 114.6, Max: 144.9, Diff: 60.6, Sum: 458.5]
  13. [Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
  14. [Termination Attempts: Min: 1, Avg: 3.0, Max: 4, Diff: 3, Sum: 12]
  15. [GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.1, Diff: 0.0, Sum: 0.2]
  16. [GC Worker Total (ms): Min: 145.4, Avg: 145.4, Max: 145.4, Diff: 0.0, Sum: 581.5]
  17. [GC Worker End (ms): Min: 792.1, Avg: 792.1, Max: 792.1, Diff: 0.0]
  18. [Code Root Fixup: 0.1 ms]
  19. [Code Root Purge: 0.0 ms]
  20. [Clear CT: 0.3 ms]
  21. [Other: 0.8 ms]
  22. [Choose CSet: 0.0 ms]
  23. [Ref Proc: 0.3 ms]
  24. [Ref Enq: 0.0 ms]
  25. [Redirty Cards: 0.3 ms]
  26. [Humongous Register: 0.0 ms]
  27. [Humongous Reclaim: 0.0 ms]
  28. [Free CSet: 0.0 ms]
  29. [Eden: 14.0M(14.0M)->0.0B(10.0M) Survivors: 0.0B->2048.0K Heap: 14.0M(192.0M)->2242.0K(192.0M)]
  30. [Times: user=0.00 sys=0.00, real=0.15 secs]
  31. 2019-08-10T20:47:50.071+0800: 0.926: [GC pause (G1 Evacuation Pause) (young), 0.0090643 secs]
  32. [Parallel Time: 2.5 ms, GC Workers: 4]
  33. [GC Worker Start (ms): Min: 926.3, Avg: 926.4, Max: 926.4, Diff: 0.0]
  34. [Ext Root Scanning (ms): Min: 0.2, Avg: 0.3, Max: 0.6, Diff: 0.4, Sum: 1.2]
  35. [Update RS (ms): Min: 0.0, Avg: 0.2, Max: 0.4, Diff: 0.4, Sum: 0.7]
  36. [Processed Buffers: Min: 0, Avg: 0.5, Max: 1, Diff: 1, Sum: 2]
  37. [Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
  38. [Code Root Scanning (ms): Min: 0.0, Avg: 0.1, Max: 0.6, Diff: 0.6, Sum: 0.6]
  39. [Object Copy (ms): Min: 1.6, Avg: 1.8, Max: 1.9, Diff: 0.4, Sum: 7.3]
  40. [Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
  41. [Termination Attempts: Min: 1, Avg: 3.3, Max: 6, Diff: 5, Sum: 13]
  42. [GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
  43. [GC Worker Total (ms): Min: 2.4, Avg: 2.5, Max: 2.5, Diff: 0.0, Sum: 9.8]
  44. [GC Worker End (ms): Min: 928.8, Avg: 928.8, Max: 928.8, Diff: 0.0]
  45. [Code Root Fixup: 0.1 ms]
  46. [Code Root Purge: 0.0 ms]
  47. [Clear CT: 0.1 ms]
  48. [Other: 6.4 ms]
  49. [Choose CSet: 0.0 ms]
  50. [Ref Proc: 0.2 ms]
  51. [Ref Enq: 0.0 ms]
  52. [Redirty Cards: 0.1 ms]
  53. [Humongous Register: 0.0 ms]
  54. [Humongous Reclaim: 0.0 ms]
  55. [Free CSet: 0.0 ms]
  56. [Eden: 10.0M(10.0M)->0.0B(17.0M) Survivors: 2048.0K->2048.0K Heap: 12.2M(192.0M)->4305.5K(384.0M)]
  57. [Times: user=0.00 sys=0.00, real=0.01 secs]

young GC:[GC pause (G1 Evacuation Pause)(young)

initial-mark:[GC pause (Metadata GC Threshold)(young)(initial-mark) (参数:InitiatingHeapOccupancyPercent)

mixed GC:[GC pause (G1 Evacuation Pause)(Mixed) (参数:G1HeapWastePercent)

full GC:[Full GC (Allocation Failure)(无可用region)

注:G1内部,前面提到的混合GC是非常重要的释放内存机制,它避免了G1出现Region没有可用的情况,否则就会触发 FullGC事件。CMSParallelSerial GC都需要通过Full GC去压缩老年代并在这个过程中扫描整个老年代。G1 Full GC算法和Serial GC收集器完全一致。当一个Full GC发生时,整个Java堆执行一个完整的压缩,这样确保了最大的空余内存可用。G1Full GC是一个单线程,它可能引起一个长时间的停顿时间,G1的设计目标是减少Full GC__,满足应用性能目标。

查看发生MixedGC的阈值:jinfo -flag InitiatingHeapOccupancyPercent 进程ID调优:

第一次调优,设置Metaspace大小:增大元空间大小-XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=64M
第二次调优,添加吞吐量和停顿时间参数:-XX:GCTimeRatio=99 -XX:MaxGCPauseMillis=10

五、JVM内存的参数配置与调优

在通过一张图来了解如何通过参数来控制各区域的内存大小 GC调优 - 图4
堆内存的接口和内存限制参数
GC调优 - 图5

六、GC常用参数

1.堆栈设置


-Xss:每个线程的栈大小

-Xms:初始堆大小,默认物理内存的1/64

-Xmx:最大堆大小,默认物理内存的1/4

-Xmn:新生代大小

-XX:NewSize:设置新生代初始大小

-XX:MaxNewSize设置新生代最大空间大小

-XX:NewRatio:默认2表示新生代占年老代的1/2,占整个堆内存的1/3。

-XX:SurvivorRatio:默认8表示一个survivor区占用1/8的Eden内存,即1/10的新生代内存。

-XX:PermSize设置永久代最小空间大小

-XX:MaxPermSize设置永久代最大空间大小

-XX:MetaspaceSize:设置元空间大小

-XX:MaxMetaspaceSize:设置元空间最大允许大小,默认不受限制,JVM Metaspace会进行动态扩展。

除了上面两个指定大小的选项以外,还有两个与 GC 相关的属性:
-XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集
-XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集

注:没有直接设置老年代的参数,但是可以设置堆空间大小和新生代空间大小两个参数来间接控制**老年代空间大小=堆空间大小-年轻代大空间大小_**

2.垃圾回收统计信息


-XX:+PrintGC

-XX:+PrintGCDetails

-XX:+PrintGCTimeStamps

-Xloggc:filename

3.收集器设置


-XX:+UseSerialGC:设置串行收集器

-XX:+UseParallelGC:设置并行收集器

-XX:+UseParallelOldGC:老年代使用并行回收收集器

-XX:+UseParNewGC:在新生代使用并行收集器

-XX:+UseParalledlOldGC:设置并行老年代收集器

-XX:+UseConcMarkSweepGC:设置CMS并发收集器

-XX:+UseG1GC:设置G1收集器

-XX:ParallelGCThreads:设置用于垃圾回收的线程数

4.并行收集器设置


-XX:ParallelGCThreads:设置并行收集器收集时使用的CPU数。并行收集线程数。

-XX:MaxGCPauseMillis:设置并行收集最大暂停时间

-XX:GCTimeRatio:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)

5.CMS收集器设置


-XX:+UseConcMarkSweepGC:设置CMS并发收集器

-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。

-XX:ParallelGCThreads:设置并发收集器新生代收集方式为并行收集时,使用的CPU数。并行收集线程数。

-XX:CMSFullGCsBeforeCompaction:设定进行多少次CMS垃圾回收后,进行一次内存压缩

-XX:+CMSClassUnloadingEnabled:允许对类元数据进行回收

-XX:UseCMSInitiatingOccupancyOnly:表示只在到达阀值的时候,才进行CMS回收

-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况

-XX:ParallelCMSThreads:设定CMS的线程数量

-XX:CMSInitiatingOccupancyFraction:设置CMS收集器在老年代空间被使用多少后触发

-XX:+UseCMSCompactAtFullCollection:设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片的整理

6.G1收集器设置


-XX:+UseG1GC:使用G1收集器

-XX:ParallelGCThreads:指定GC工作的线程数量

-XX:G1HeapRegionSize:指定分区大小(1MB~32MB,且必须是2的幂),默认将整堆划分为2048个分区

-XX:GCTimeRatio:吞吐量大小,0-100的整数(默认9),值为n则系统将花费不超过1/(1+n)的时间用于垃圾收集

-XX:MaxGCPauseMillis:目标暂停时间(默认200ms)

-XX:G1NewSizePercent:新生代内存初始空间(默认整堆5%)

-XX:G1MaxNewSizePercent:新生代内存最大空间

-XX:TargetSurvivorRatio:Survivor填充容量(默认50%)

-XX:MaxTenuringThreshold:最大任期阈值(默认15)

-XX:InitiatingHeapOccupancyPercen:老年代占用空间超过整堆比IHOP阈值(默认45%),超过则执行混合收集

-XX:G1HeapWastePercent:堆废物百分比(默认5%)

-XX:G1MixedGCCountTarget:参数混合周期的最大总次数(默认8)

七、参考资料

jvm系列之内存模型:http://blog.leanote.com/post/zhangyue/jvm%E7%B3%BB%E5%88%97%E4%B9%8B%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B