排查思路

当页面使用卡慢时,首先我们需要排除网速慢服务器性能差等客观原因;
只剩下主观原因之后,我们需要依次排查 CPU占用、JVM内存泄漏、死锁、线程频繁切换、频繁GC、IO占用等常见原因,然后再去精确定位到具体的代码或者配置问题

前置知识

常用命令

系统资源相关

top 命令

top命令用于查看进程占用的系统资源占用状况,top -Hp pid用于查看指定进程的相关线程的系统资源占用状况

vmstat 命令

vmstat用于指定周期和采集次数的虚拟内存检测工具,可以统计 CPU,swap的使用情况,也用于观察进程的上下文切换
字段说明如下:

  • r:运行中的进程数量(当数量大于CPU核数表示有线程阻塞)
  • b:等待IO的进程数量
  • swpd:使用虚拟内存大小
  • free: 空闲物理内存大小
  • buff: 用作缓冲的内存大小(内存和硬盘的缓冲区)
  • cache: 用作缓存的内存大小(CPU 和内存之间的缓冲区)
  • si: 每秒从交换区写到内存的大小,由磁盘调入内存
  • so: 每秒写入交换区的内存大小,由内存调入磁盘
  • bi: 每秒读取的块数
  • bo: 每秒写入的块数
  • in: 每秒中断数,包括时钟中断。
  • cs: 每秒上下文切换数。
  • us: 用户进程执行时间百分比(user time)
  • sy: 内核系统进程执行时间百分比(system time)
  • wa: IO 等待时间百分比
  • id: 空闲时间百分比

    pidstat 命令

    JVM 相关

  • jps:
    查看所有 Java 进程

  • jstat:
    用于收集 HotSpot 虚拟机各方面运行数据
  • jinfo:
    显示虚拟机配置信息
  • jmap:
    生成堆转储快照
  • jhat:
    用于分析 heapdump 文件,可以在浏览器上查看分析结果
  • jstack:
    生成虚拟机当前时刻线程的堆栈快照

常见问题

CPU 占用过高

  1. 先使用 top 命令查看CPU使用最高的进程号
  2. 使用 ps H -eo pid,tid,%cpu | grep 进程号,可以查看当前进程中所有运行的线程信息和CPU占用率,这样我们可以过滤出占用CPU最多的线程号
    使用 top -Hp 进程号也可以获得相关线程信息
  3. 执行 printf '%x' 线程id获取16进制的线程id ,称为nid
  4. 执行 jstack 进程id | grep -A 20 nid 定位到出现问题的方法

    死锁

  5. jstack 进程id | grep -A 20 deadlock 定位死锁位置

    内存溢出

  6. 执行 jps 查找 java 进程号

  7. 执行 jmap -heap 进程号 获取java堆空间信息
  8. 执行 jmap -histo:live 进程号查看进程中存活的对象
  9. 使用 jConsole 工具
  10. 使用 jVisualVM 工具

jVisualVM

  1. JAVA_OPTS="$JAVA_OPTS -Djava.rmi.server.hostname=10.44.237.96"
  2. JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote"
  3. JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.rmi.port=1099"
  4. JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.port=1099"
  5. JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.authenticate=false"
  6. JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.ssl=false"

线程切换

  1. 线程频繁切换会导致大量的CPU资源浪费在寄存器、内核、以及虚拟内存的保存和恢复上,导致系统的整体性能下降

当CPU被占用率较高,但是通过 top -Hp pid 命令查看线程占用状态时,各个线程的占用率都比较平均,此时可以考虑时线程频繁切换导致的系统性能问题

  1. 使用 vmstat 1 10 查看线程切换信息(1秒打印一次,总共打印10次)
    主要关注4个指标:
    r:表示等待的进程数量
    cs:表示每秒上下文切换次数
    us:用户态占用CPU的时间比例
    sy:内核态占用CPU的时间比例

  2. 使用 pidstat -p pid -w 1 10 来查看指定 java 进程内部的线程运行情况,pid指的是运行的java程序进程号
    主要关注2指标:
    nvcswch/s:线程每秒切换的次数
    pid:线程的十进制号

接着将进程号转换为16进制,并配合 jstack 命令即可定位到相关的代码位置

Arthas

Arthas(阿尔萨斯)是阿里推出的一款性能检测工具;可以做到无侵入地监控应用的运行情况
Arthas官方文档

常用命令

dashboard

显示系统当前实时面板
详见:https://arthas.aliyun.com/doc/dashboard.html

thread

  • thread pid 显示指定线程的堆栈信息
  • thread -n pid 显示最忙碌的前n个线程并打印堆栈
  • thread -b找出当前阻塞其他线程的线程
  • thread -i time指定采样间隔

thread -n 3 -i 1000每秒统计最忙的前三个线程

jad

  • jad java.lang.String反编译已经被加载的源码和类加载信息
  • jad --source-only只打印源码

    watch

  • watch com.xx.Demo doDemo returnObj查看 demo 类的 doDemo 方法的返回值

watch demo.MathGame primeFactors “{params,target,returnObj}” -x 2 -b -s -n 2

  • params:本次调用参数列表
  • target:本次调用类的实例
  • returnObj:本次调用的返回对象
  • -x:属性遍历深度,默认为1
  • -b:在函数执行前观察
  • -s:在函数返回后观察
  • -n:执行次数

    trace

    查看方法的调用路径
    trace demo.MathGame run
    查看 MathGame 类中 run 方法调用其它方法的调用路径
    run() → primeFactors() → print()

    stack

    查看方法的被调用路径
    stack demo.MathGame primeFactors
    查看 MathGame 中 primeFactors 方法被调用路径

参考

JavaGuide 手把手教你定位常见Java性能问题