JVM系列之性能调优案例

1、调优基本问题

1.1、为什么要调优?

  • 删除线格式 防止出现OOM,进行JM规划和预调优
  • 解决程序运行中各种OOM
  • 减少Full GC出现的频率,解决运行慢、卡顿问题

1.2、调优的大方向

  • 合理地编写代码
  • 充分并合理的使用硬件资源
  • 合理地进行JVM调优

1.3、不同阶段的考虑

  • 上线前
  • 项目运行阶段
  • 线上出现OOM

1.4、调优监控的依据

  • 运行日志
  • 异常堆栈
  • GC日志
  • 线程快照
  • 堆转储快照

1.5、性能优化的步骤

1.5.1、第1步:熟悉业务场景

1.5.2、第2步(发现问题)︰性能监控

一种以非强行或者入侵方式收集或查看应用运营性能数据的活动,监控通常是指一种在生产、质量评估或者开发环境下实施的带有预防或主动性的活动。
当应用相关干系人提出性能问题却没有提供足够多的线索时。
首先我们需要进行性能监控,随后是性能分析。
监控前,设置好回收器组合,选定CPU(主频越高越好),设置年代比例,设置日志参数(生产环境中通常不会只设置一个日志文件)。比如:

-Xloggc:/opt/xxx/logs/xxx-xxx-gc-%t.log 
-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=5 
-XX:GCLogFileSize=20M 
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps 
-XX:+PrintGCCause
1.5.2.1、可能出现的情况
  • GC频繁
  • cpu load过高
  • OOM
  • 内存泄漏
  • 死锁
  • 程序响应时间较长

1.5.2、第3步(排查问题):性能分析团

  • 打印GC日志,通过GCviewer或者http://gceasy.io来分析日志管息
  • 灵活运用命令行工具,jstack, jmap, jinfo等
  • dump出堆文件,使用内存分析工具分析文件
  • 使用阿里Arthas,或jconsole,JVisualVM来实时查看JVM状态
  • jstack查看堆栈信息

1.5.4、第4步(解决问题):性能调优

一种为改善应用响应性或吞吐量而更改参数、源代码、属性配置的活动,性能调优是在性能监控、性能分析之后的活动。

  • 适当增加内存,根据业务背景选择垃圾回收器
  • 优化代码,控制内存使用
  • 增加机器,分散节点压力
  • 合理设置线程池线程数量
  • 使用中间件提高程序效率,比如缓存,消息队列等
  • 其他…

1.6、总结

调优,从业务场景开始,没有业务场景的调优都是耍流氓,无监控,不调优!

使用的是Jmeter进行压测。

1.7、面试题

  • 如何优化减少Full GC?(阿里-闲鱼)
  • 当出现了内存溢出,你怎么排错。(京东)
  • 有实际的VM性能调优案例吗?重点需要关注哪些核心参数?(滴滴)
  • OOM说一下?怎么排查?哪些会导致OOM? OOM出现在什么时候(腾讯)
  • JVM性能调优都做了什么?(支付宝)
  • 有做过JVM内存优化吗?(小米)
  • JVM的编译优化(蚂蚁金服)
  • JVM性能调优都做了什么(蚂蚁金服)
  • JVM怎样调优,堆内存栈空间设置多少合适…(蚂蚁金服)
  • JVM相关的分析工具使用过的有哪些?具体的性能调优步骤如何(蚂蚁金服)
  • 如何进行JVM调优?有哪些方法?(阿里)
  • JVM如何调优、参数怎么调?(字节跳动)
  • 每秒几十万并发的秒杀系统为什么会频繁发生GC?(京东)
  • 日均百万级交易系统如何优化JVM?(京东)
  • 线上生产系统OOM如何监控及定位与解决?(京东)
  • 高并发系统如何基于G1垃圾回收器优化性能?(京东)

2、性能优化案例1:调整堆大小提高服务的吞吐量

初始配置

export CATALINA_OPTS="$CATALINA_OPTS -Xms30m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:SurvivorRatio=8"
export CATALINA_OPTS="$CATALINA_OPTS -Xmx120m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseParallelGC"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+PrintGCDetails"
export CATALINA_OPTS="$CATALINA_OPTS -XX:MetaspaceSize=64m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+PrintGCDateStamps"
export CATALINA_OPTS="$CATALINA_OPTS -Xloggc:/opt/model/tomcat-8.5/logs/gc.log"

在这里插入图片描述

在这里插入图片描述

优化配置

export CATALINA_OPTS="$CATALINA_OPTS -Xms120m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:SurvivorRatio=8"
export CATALINA_OPTS="$CATALINA_OPTS -Xmx120m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseParallelGC"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+PrintGCDetails"
export CATALINA_OPTS="$CATALINA_OPTS -XX:MetaspaceSize=64m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+PrintGCDateStamps"
export CATALINA_OPTS="$CATALINA_OPTS -Xloggc:/opt/model/tomcat-8.5/logs/gc.log"

在这里插入图片描述

在这里插入图片描述

3、性能优化案例2:合理配置堆内存

在案例1中我们讲到了增加内存可以提高系统的性能而且效果显著,那么随之带来的一个问题就是,我们增加多少内存比较合适?如果内存过大,那么如果产生FullGC的时候,GC时间会相对比较长,如果内存较小,那么就会频繁的触发GC,在这种情况下,我们该如何合理的适配堆内存大小呢?
分析:
依据的原则是根据Java Performance里面的推荐公式来进行设置。

在这里插入图片描述

3.1、推荐配置

Java整个堆大小设置:
Xmx和Xms设置为老年代存活对象的3-4倍,即FullGC之后的老年代内存占用的3-4倍。
方法区(永久代 PermSize和MaxPermSize或元空间MetaspaceSize和MaxMetaspaceSize)设置为老年代存活对象的1.2-1.5倍。
年轻代Xmn的设置为老年代存活对象的1-1.5倍。
老年代的内存大小设置为老年代存活对象的2-3倍。

但是,上面的说法也不是绝对的,也就是说这给的是一个参考值,根据多种调优之后得出的一个结论,大家可以根据这个值来设置一下我们的初始化内存,在保证程序正常运行的情况下,我们还要去查看GC的回收率,GC停顿耗时,内存里的实际数据来判断,Full GC是基本上不能有的,如果有就要做内存Dump分析,然后再去做一个合理的内存分配。
我们还要注意到一点就是,上面说的老年代存活对象怎么去判定。

3.2、如何计算老年代存活对象

如何计算老年代存活对象

3.2.1、方式1:查看日志

推荐/比较稳妥!
JVM参数中添加GC日志,GC日志中会记录每次FullGC之后各代的内存大小,观察老年代GC之后的空间大小。可观察一段时间内(比如2天)的FullGC之后的内存情况,根据多次的FullGC之后的老年代的空间大小数据来预估FullGC之后老年代的存活对象大小(可根据多次FullGC之后的内存大小取平均值)。

3.2.2、方式2:强制触发FullGc

方式2:强制触发FullGC,会影响线上服务,慎用!
方式1的方式比较可行,但需要更改JVM参数,并分析日志。同时,在使用CMS回收器的时候,有可能不能触发FullGC,所以日志中并没有记录FullGC的日志。在分析的时候就比较难处理。所以,有时候需要强制触发一次FullGC,来观察FullGC之后的老年代存活对象大小。

注:强制触发FullGC,会造成线上服务停顿(STW),要谨慎!建议的操作方式为,在强制FullGC前先把服务节点摘除,FullGC之后再将服务挂回可用节点,对外提供服务,在不同时间段触发FullGC,根据多次FullGC之后的老年代内存情况来预估FullGC之后的老年代存活对象大小

如何强制触发Full GC?
1、jmap -dump:live,format=b,file=heap.bin 将当前的存活对象dump到文件,此时会触发FullGC
2、jmap -histo:live 打印每个class的实例数目,内存占用,类全名信息…live子参数加上后,只统计活的对象数量.此时会触发FullGC
3、在性能测试环境,可以通过Java监控工具来触发FullGC,比如使用VisualVM和JConsole,VisualVM集成了JConsole,VisualVM或者JConsole上面有一个触发GC的按钮。

3.2.3、你会估算GC频率吗?

正常情况我们应该根据我们的系统来进行一个内存的估算,这个我们可以在测试环境进行测试,最开始可以将内存设置的大一些,比如4G这样,当然这也可以根据业务系统估算来的。
比如从数据库获取一条数据占用128个字节,需要获取1000条数据,那么一次读取到内存的大小就是((128 B/1024 Kb/1024M)* 1000 = 0.122M,那么我们程序可能需要并发读取,比如每秒读取100次,那么内存占用就是0.122100 = 12.2M,如果堆内存设置1个G,那么年轻代大小大约就是333M,那么333M80%/12.2M =21.84s,也就是说我们的程序几乎每分钟进行两到三次youngGC。这样可以让我们对系统有一个大致的估算。

3.2.4、案例分析

现在我们通过idea启动springboot工程,我们将内存初始化为1024M。我们这里就从1024M的内存开始分析我们的GC日志,根据我们上面的一些知识来进行一个合理的内存设置。

压测接口

    @RequestMapping( " /getData")
    public List<People> getProduct(){
    
    
        List<People> peopleList = peoplesevice.getPeopleList();
        return peopleList;
    }

JVM设置如下:

-XX:+PrintGCDetails 
-XX:MetaspaceSize=64m
-Xss512K
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=heap/heapdump3.hprof 
-XX:SurvivorRatio=8
-XX:+PrintGCDateStamps 
-Xms1024M -Xmx1024M
-Xloggc:log/gc-oom3.log

强制Full GC

在这里插入图片描述
查看堆空间占比情况

jmap -heap 12324

在这里插入图片描述
安照官网上面推荐的老年代的内存占比,老年代应该设置为已使用的3到4倍

-XX:+PrintGCDetails 
-XX:MetaspaceSize=64m
-Xss512K
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=heap/heapdump3.hprof 
-XX:SurvivorRatio=8
-XX:+PrintGCDateStamps 
-Xms80M -Xmx80M
-Xloggc:log/gc-oom3.log

4、性能优化案例3:CPU占用很高排查方案

CPU占用很高问题排查过程:

  1. ps aux | grep java查看到当前java进程使用cpu、内存、磁盘的情况获取使用量异常的进程
  2. top -Hp 进程pid 检查当前使用异常线程的pid
  3. 把线程pid变为16进制如31695 - 》 7bcf 然后得到Ox7bcf
  4. jstack 进程的pid | grep -A20 Ox7bcf 得到相关进程的代码 或者导出日志为文件,根据16进制的线程号在日志文件中搜索(jstack 进程的pid >xx.log)

5、性能优化案例4:G1并发执行的线程数对性能的影响

JVM参数设置

export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseG1GC"
export CATALINA_OPTS="$CATALINA_OPTS -Xms30m"
export CATALINA_OPTS="$CATALINA_OPTS -Xmx30m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+PrintGCDetails"
export CATALINA_OPTS="$CATALINA_OPTS -XX:MetaspaceSize=64m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+PrintGCDateStamps"
export CATALINA_OPTS="$CATALINA_OPTS -Xloggc:/opt/tomcat8.5/logs/gc.log"
export CATALINA_OPTS="$CATALINA_OPTS -xx:ConcGCThreads=1"

说明:最后一个参数可以在使用G1GC测试初始并发GCThreads之后再加上。初始化内存和最大内存调整小一些,目的发生 FullGc,关注GC时间,关注点是:GC次数,GC时间,以及Jmeter的平均响应时间
在这里插入图片描述
在这里插入图片描述

JVM参数设置修改之后

export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseG1GC"
export CATALINA_OPTS="$CATALINA_OPTS -Xms30m"
export CATALINA_OPTS="$CATALINA_OPTS -Xmx30m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+PrintGCDetails"
export CATALINA_OPTS="$CATALINA_OPTS -XX:MetaspaceSize=64m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+PrintGCDateStamps"
export CATALINA_OPTS="$CATALINA_OPTS -Xloggc:/opt/tomcat8.5/logs/gc.log"
export CATALINA_OPTS="$CATALINA_OPTS -xx:ConcGCThreads=2"

在这里插入图片描述
在这里插入图片描述

6、性能优化案例5:日均百万级订单交易系统如何设置JVM参数

在这里插入图片描述

7、性能优化案例6 网站问题分析

有一个50万PV的资料类网站(从磁盘提取文档到内存)原服务器是32位的,1.5G的堆,用户反馈网站比较缓慢。因此公司决定升级,新的服务器为64位,16G的堆内存,结果用户反馈卡顿十分严重,反而比以前效率更低了!

7.1、为什么原网站慢?

频繁的GC,STw时间比较长,响应时间慢!

7.2、为什么会更卡顿?

内存空间越大,FGC时间更长,延迟时间更长

732、怎么处理

垃圾回收器: parallel Gc ;ParNew + CMS ; G1
配置Gc参数:-XX:MaxGCPauseMillis 、-XX:ConcGCThreads根据log日志、dump文件分析,优化内存空间的比例
jstat jinfo jstack jmap

8、系统内存飙高,如何查找问题?

  1. 一方面:jmap -heap . jstat . …; gc日志情况
  2. 另一方面:dump文件分析

猜你喜欢

转载自blog.csdn.net/prefect_start/article/details/124145643