本文为博主原创,未经允许不得转载:

  系统上线压测,需要了解系统的瓶颈以及吞吐量,并根据压测数据进行对应的优化。

  对压测进行 JVM 性能优化,有两条思路:

  第一种情况 : 使用压测工具 jmeter 进行小量并发业务测试, 通过 top 命令查看cpu是否会急速飙升。若在 小并发量压测时或单独调试时,出现 cpu 性能飙升,

    那就需要对对应的业务接口进行代码分析,分析消耗cpu 的原因。

  第二种情况:使用压测工具jmeter ,jvisulam 等进行大并发量业务测试,并使用 top 命令实时监控 cpu ,以及内存的使用情况,查看 垃圾回收

    MinorGC 和 Full GC 的频率和情况。根据 GC 的数据进行对应的JVM参数设置,提高系统的可用性。

如何进行第一种情况调优:

  1. CPU 迅速飙升,在业务服务器上进行监控,

    1.通过 top 命令定位到占用 CPU 和内存 最高的线程

    2. 并将对应的线程转为16进制,通过jstack 查询该线程的堆栈信息,从而定位到对应的代码,并进行代码分析

    3. 进行代码性能优化

第二种情况调优:

  第二种情况调优属于 JVM 参数性能调优,主要通过设置 堆(新生代和老年代)的大小以及GC 垃圾回收器,从而提高服务的性能。

    该调优需要在压测时,需要对该 java 进程实时的垃圾回收情况进行监控。分析在压测业务吞吐量相对稳定时,GC的回收情况。  

    1. 使用 jstat 命令对指定的java 进程进行实时监控。并计算出一些关键数据。并给自己的系统设置一些初始性的JVM 参数:

      比如 堆内存大小,年轻代大小,Eden和Survivor的比例,老年代的大小,大对象的阈值,大龄对象进入老年代的阈值等。 

    2. 查看年轻代对象的增长速率:

      可以执行命令 jstat -gc pid 1000 10 (每隔1秒执行1次命令,共执行10次),通过观察EU(eden区的使用)来估算每秒eden大概新增多少对象,

    如果系统负载不高,可以把频率1秒换成1分钟,甚至10分钟来观察整体情况。注意,一般系统可能有高峰期和日常期,所以需要在不

    同的时间分别估算不同情况下对象增长速率。

    3. Young GC的触发频率和每次耗时

      知道年轻代对象增长速率我们就能推根据eden区的大小推算出Young GC大概多久触发一次,Young GC的平均耗时可以通过 YGCT/YGC公式算出,
    根据结果我们大概就能知道系统大概多久会因为Young GC的执行而卡顿多久。
 
    4. 每次Young GC后有多少对象存活和进入老年代
      这个因为之前已经大概知道Young GC的频率,假设是每5分钟一次,那么可以执行命令 jstat -gc pid 300000 10 ,观察每次结果eden,survivor和老年代
    使用的变化情况,在每次gc后eden区使用一般会大幅减少,survivor和老年代都有可能增长,这些增长的对象就是每次Young GC后存活的对象,同时还可以看
    出每次Young GC后进去老年代大概多少对象,从而可以推算出老年代对象增长速率。

    5. Full GC的触发频率和每次耗时

      知道了老年代对象的增长速率就可以推算出Full GC的触发频率了,Full GC的每次耗时可以用公式 FGCT/FGC 计算得出。

    6. 优化思路其实简单来说就是尽量让每次Young GC后的存活对象小于Survivor区域的50%,都留存在年轻代里。

      尽量别让对象进入老年代。尽量减少Full GC的频率,避免频繁Full GC对JVM性能的影响。

    在调试JVM 的堆大小时,可以根据JVM 堆得内存模型图进行推算参数值:

    JVM 性能调优 及 为什么要减少 Full GC-风君雪科技博客

  为什么要减少 Full GC:

    通过设置JVM 的参数,减少Full GC 触发的频率,因为 Full GC 会导致 STW :top一the一World,简称STW,指的是Gc事件发生过程中,

  会产生应用程序的停顿。停顿产生时整个应用程序线程都会被暂停,没有任何响应,有点像卡死的感觉,这个停顿称为STW。

    为什么要减少 Full GC ,因为 Young GC 每次扫描的对象少,且对象的生命周期较短,容器GC ,而 Full GC 不仅需要扫描清理老年代的

  垃圾对象,还需要清理 metaspace和新生代的垃圾对象,由于老年代中所保存的对象是生命周期较长的对象,不易清理,比较耗时,这就会

  导致 STW 时间变长,服务不可用或卡顿的现象也就越长。 

  

  常用的 JVM 参数配置:

     

参数 说明 实例
-Xms 初始堆大小,默认物理内存的1/64 -Xms512M
-Xmx 最大堆大小,默认物理内存的1/4 -Xms2G
-Xmn 新生代内存大小,官方推荐为整个堆的3/8 -Xmn512M
-Xss 线程堆栈大小,jdk1.5及之后默认1M,之前默认256k -Xss512k
-XX:NewRatio=n 设置新生代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4 -XX:NewRatio=3
-XX:SurvivorRatio=n 年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:8,表示Eden:Survivor=8:1:1,一个Survivor区占整个年轻代的1/8 -XX:SurvivorRatio=8
-XX:PermSize=n 永久代初始值,默认为物理内存的1/64 -XX:PermSize=128M
-XX:MaxPermSize=n 永久代最大值,默认为物理内存的1/4 -XX:MaxPermSize=256M
-verbose:class 在控制台打印类加载信息  
-verbose:gc 在控制台打印垃圾回收日志  
-XX:+PrintGC 打印GC日志,内容简单  
-XX:+PrintGCDetails 打印GC日志,内容详细  
-XX:+PrintGCDateStamps 在GC日志中添加时间戳  
-Xloggc:filename 指定gc日志路径 -Xloggc:/data/jvm/gc.log
-XX:+UseSerialGC 年轻代设置串行收集器Serial  
-XX:+UseParallelGC 年轻代设置并行收集器Parallel Scavenge  
-XX:ParallelGCThreads=n 设置Parallel Scavenge收集时使用的CPU数。并行收集线程数。 -XX:ParallelGCThreads=4
-XX:MaxGCPauseMillis=n 设置Parallel Scavenge回收的最大时间(毫秒) -XX:MaxGCPauseMillis=100
-XX:GCTimeRatio=n 设置Parallel Scavenge垃圾回收时间占程序运行时间的百分比。公式为1/(1+n) -XX:GCTimeRatio=19
-XX:+UseParallelOldGC 设置老年代为并行收集器ParallelOld收集器  
-XX:+UseConcMarkSweepGC 设置老年代并发收集器CMS  
-XX:+CMSIncrementalMode 设置CMS收集器为增量模式,适用于单CPU情况。