引子
- 系统出现性能问题一筹莫展?
- 引入一堆框架和库之后,不知道到底开了多少线程?
- 内存飙升,甚至OOM,但是不知道内存耗费在了哪里?
- 维护旧系统,使用三方脚本,不知道对方到底设置了哪些参数?
- 耗时统计只局限在自己的代码里,对SDK中方法耗时无从分析?
性能分析第一步——采集指标
监控性能的工具
- 命令行工具:jstat、jcmd、jmap……
- 可视化工具:jvisualvm、JMC(Java Mission Control)、JProfiler
- 运维监控:Prometheus
如何开启jvisualvm远程监控
添加JVM启动参数:
- -Djava.rmi.server.hostname=xx.xx.xx.xx(机器的IP)
- -Dcom.sun.management.jmxremote
- -Dcom.sun.management.jmxremote.port=11099(jmx端口)
- -Dcom.sun.management.jmxremote.authenticate=false
- -Dcom.sun.management.jmxremote.ssl=false
示例:
java -Djava.rmi.server.hostname=xxx.xx.xx.xx -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=11099 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -jar xxx.jar
对于完全可执行的spring boot jar包,可以在.conf
中通过JAVA_OPTS
指定JVM启动参数
如何监控GC
-
在jvisualvm中安装Visual GC插件:https://jingyan.baidu.com/article/6b182309004514fa58e159a3.html
-
在服务器上启动jstatd:https://blog.csdn.net/liupeifeng3514/article/details/78998161/
-
创建文件
jstatd.all.policy
-
grant codebase "file:${java.home}/../lib/tools.jar" { permission java.security.AllPermission; };
-
jstatd -J-Djava.rmi.server.hostname=172.29.12.33 -J-Djava.security.policy=jstatd.all.policy
-
看看jvisualvm可以做什么
- 查看JVM参数
- 最大内存、起始内存
- 查看系统属性
- user.timezone=Asia/Shanghai
- 监控资源使用情况
- CPU(CPU占比、GC占比)
- 堆(最大堆空间,使用堆空间)
- 类(类加载数量、占用空间)
- 线程(线程数量)
- 线程运行状态
- 分析采样
- CPU采样
- 内存采样
- 远程命令
- 强制GC
- 线程Dump
- 堆Dump
- 监控GC情况
实战:一个归档程序的性能优化之路
场景还原
- 线上运行了一个归档程序,定期把S3中的小文件归档成大文件,再存入S3中
- 这个归档程序运行一次大概需要12小时,并且CPU消耗也比较大,大约在60%70%
- 有一天突然发现,当小文件数量从200W涨到300W时,处理耗时增加了1个小时,CPU直接飙升到90%+
- 归档本身是一个IO密集型的程序,如此CPU消耗十分不正常
性能分析
- 当时打开JMX,发现CPU周期性飙升,并且伴有垃圾回收的CPU飙升
- 堆空间占用率十分高,几乎都要打满了
- 查看GC后发现,老年代几乎满了,频繁触发Full GC
- Full GC : Young GC = 10 : 1
- 执行堆Dump,准备进行内存分析
内存分析
- 打开eclipse MAT,对Dump文件进行分析
- 马上得到异常对象
- 反追代码,发现对List没有进行清理,造成内存泄漏
代码优化
- 使用阻塞队列控制生产速度