1、前言
所有JAVA服务的线上问题从系统表象来看归结起来如下:
- CPU:使用峰值突然飙高
- 内存:内存溢出、泄漏
- 磁盘:空间满了
- 网络:流量异常
- 其他:FullGC等
基于上述现象,我们将线上问题分成两大类:系统异常、业务服务异常
1.1 系统异常
常见的系统异常现象包括:CPU占用率过高、CPU上下文切换频率次数较高、磁盘满了、磁盘IO过于频繁、网络流量异常(连接数过多)、系统可用内存长期处于较低值(导致OOM killer)等。这些问题可以通过top(CPU)、free(内存)、df(磁盘)、dstat(网络流量)、pstack、vmstat、trace(底层调用)等工具获取系统异常现象数据。
此外,如果对系统以及应用进行排查后,均为发现异常现象的根本原因,那么也有可能是因为外部基础设施IAAS平台所引发的问题。例如你的应用只有某个区域如上海应用访问系统时发生服务不可用现象,那么极有可能是运营商网络或者云服务商发生的一些故障导致的。
1.2 业务服务异常
常见的业务服务异常现象包括:PV量过高、服务调用耗时异常、线程死锁、多线程并发问题、频繁进行FullGC、异常安全攻击扫描等。
2 问题定位
我们一般会采用排除法,从外部排查到内部的方式来定位线上服务问题。
- 首先我们要排出其他进程(除主进程之外)可能引起的故障问题
- 其次排除业务应用可能引起的故障问题
- 最后可以考虑是否为运营商或者云服务提供商所引起的故障
3 定位流程
3.1 系统异常排查流程
3.2 业务排查流程
4 linux常用的性能分析工具
linux常用的性能分析工具使用包括:top(cpu)、free(内存)、df(磁盘)、dstat(网络流量)、pstack、vmstat、strace(底层系统调用)等
4.1 CPU
cpu是系统重要的监控指标,能够分析系统的整体运行状况。监控指标一般包括运行队列、CPU使用率、上下文切换等。
top命令是Linux下常用的CPU性能分析工具,能够实时显示系统中各个进程的资源占用状况,常用于服务端性能分析。
top命令显示了各个进程CPU的使用情况,一般CPU使用率从高到低排序输出。其中Load Average显示最近1分钟、5分钟、15分钟的系统平均负载,上图各值为0.03、0.04、0.05。
我们一般会关注CPU使用率最高的进程,正常情况下就是我们的主进程。
第七行以下:各进程的状态监控。
PID:进程id
USER:进程所有者
PR:进程优先级
NI:nice值。负值表示高优先级,正值表示低优先级
VIRT:进程使用的虚拟内存总量,单位kb。VIRT = SWAP + RES
RES:进程使用的、未被换出的物理内存大小,单位kb。RES = CODE + DATA
SHR:共享内存大小,单位kb
S:进程状态。D=不可中断的睡眠状态。R=运行。S=睡眠。T=跟踪/停止。Z=僵尸进程
%CPU:上次更新到现在的CPU时间占用百分比
%MEM:进程使用的物理内存占用百分比
TIME+:进程使用的CPU时间总计,单位1/100秒
COMMAND:进程名称
4.2 内存
内存是排查线上问题的忠涛参考依据,内存问题很多时候是英气CPU使用率较高的见解因素。
系统内存:free 是显示的当前内存的使用,-m 的意思是M字节来显示内容
free -m
部分参数说明:
total:内存总数,15883MB
used:已经使用的内存数,6299M
free:空闲的内存数,6202M
shared:当前已经废弃不用的内存数,225M
buff/cache:缓存内存数:1792M
4.3 磁盘
磁盘满了很多时候会连带引起系统服务不可用等问题
df -h
# 查看该目录下所有包含的文件夹的磁盘占用量,为每一个文件夹都分配了占用量,单位 MB
# 最小值:1
du -m /path
# 显示当前目录下的文件和文件夹所占用的磁盘空间数
du -sh *
4.4 网络
dstat命令可以集成了vmstat、iostat、netstat等工具能完成的任务
dstat -c cpu情况
-d 磁盘读写
-n 网络状况
-l 显示系统负载
-m 显示内存状况
-p 显示系统进程信息
-r 显示系统IO情况
4.5 其他
vmstat 是virtual memory statistics虚拟内存统计的缩写,是实时系统监控工具。该命令通过使用knlist子程序和 /dev/kmen伪设备驱动器访问这些数据,输出信息直接打印在屏幕。
# 查看 io 的情况(第一个参数是采样的时间间隔数,单位:秒,第二个参数是采样的次数)
vmstat 2 10 -t
r:表示运行队列,就是说多少个进程真的分到了CPU
b:表示阻塞的进程
swapd:虚拟内存已使用的大小,如果大于0,表示你的机器物理内存不足了,如果不是程序内存泄漏的原因,那么你该升级内存了或者把耗内存的任务迁移到其他机器
free:空闲的物理内存大小,我的机器总共16GB,剩余6024MB
buff:linux/uninx系统用来存储,目录里面有什么内容,权限等的缓存,我本机大概占用358MB
cache:文件缓存
si:表示由磁盘调入内存,也就是内存进入内存交换区的数量
so:表示由内存调入磁盘,也就是内存交换去入内存的数量
一般情况下,si、so都为0,如果si、so的值长期不为0,则表示系统内存不足,需要考虑是否增加系统内存
bi:从块设备读入数据的总量(读磁盘),每秒kb
bo:向块设备写入数据的总量(写磁盘),每秒kb
随机磁盘读写·的时候,这两个值越大(超过1024k),能看到CPU在IO等待的值也会越大,这里设置的bi + bo参考值为1000,如果超过1000,而且wa值比较大,则表示系统磁盘IO性能瓶颈。
in:每秒CPU的中断次数,包括时间中断
cs:上下文切换context switch
strace:strace常用来跟踪进程执行时的系统调用和所接收的信号
5 JVM定位问题工具
在JDK安装目录的bin目录下默认提供了很多有价值的命令行工具。每个小工具体积基本都比较肖,因为这些工具只是jdl/lib/tools.jar的简单封装
其中,定位排查问题时最为常用的命令包括:jps(进程)、jmap(内存)、jstack(线程)、jinfo(参数)等。
- jps:查询当前机器所有JAVA进程信息
- jmap:输出某个java进程内存情况(如:产生哪些对象及数量等)
- jstack:打印某个java线程的线程栈信息
- jinfo:用于查看jvm的配置参数
5.1 jps命令
jps用于输出当前用户启动的所有进程ID,当线上发现故障或者命令时,能够利用jps快速定位的Java进程ID
# 用于输出主启动类的完整路径
jps -l -m
当然,我们也可以使用Linux提供的查询状态命令,例如:
ps -ef|grep tomcat
我们也可以快速获取tomcat服务的进程id
5.2 jmap命令
# 输出当前进程JVM堆新生代、老年代、持久代等情况,GC使用的算法等信息
jmap -heap {pid}
# 输出当前进程内存中所有对象包含的大小
# 输出当前进程内存中所有对象实例数(instances)和大小(bytes)。如果某个业务对象实例数和大小存在异常情况,可能存在内存泄漏或者业务设计方面存在不合理之处。
jmap -histo:live {pid} | head -n 10
# jmap -dump:format=b, file= 以二进制输出当前内存的堆情况至相应文件,然后可以结合MAT等内存分析工具深入分析当前内存情况。
jmap -dump:format=b, file=/usr/local/logs/dump.hprof {pid}
一般我们要求给JVM添加参数 -XX:+Heap Dump On Out Of Memory Error OOM
确保应用发生OOM时JVM能够保存并dump出当前的内存镜像。
当然,如果你决定手动dump内存时,dump操作占据一定CPU时间片、内存资源、磁盘资源等,因此会带来一定的负面影响。
此外,dump的文件可能比较大,一般我们可以考虑使用zip命令对文件进行压缩处理,这样在下载文件时能减少带宽的开销。
下载dump文件完成之后,由于dump文件较大可将dump文件备份至指定位置或者直接删除,以释放磁盘在这块空间的占用。
5.3 jstack
某Java进程CPU占用率过高,我们想要定位到其中CPU占用率最高的线程。
(1)利用top命令查询某个进程中占CPU最高的线程pid
top -Hp {进程pid}
例如:top -Hp 13742
(2)占用率最高的线程ID为24381,将其转化为16进制(因为java native线程以16进制形式输出)
printf "%x\n" 28823
(3)利用jstack打印出java线程调用栈的信息
jstack {进程ID} |grep '0x{线程ID}' -A 50 -color
jstack {进程ID} |grep '0x{线程ID}' -C20 -color
(该图片为网上随意找的图片,因为我的机子java进程少单线程且运行稳定,没调用栈信息)
5.4 jinfo
# 查看某个JVM参数值
jinfo -flag ReservedCodeCacheSize 28461
jinfo -flag MaxPermSize 28461
5.5 jstat
jstat -gc pid jstat -gcutil `pgrep -u admin java`
6 内存分析工具 MAT
6.1 什么是MAT?
MAT(memory Analyzer Tool),一个基于Eclipse的内存分析工具,是一个快速、功能丰富的JAVA heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗。
使用内存分析工具从众多的对象中进行分析,快速的计算出在内存中对象的占用大小,看看是谁阻止了垃圾收集器的回收工作,并可以通过报表直观的查看到可能造成这种结果的对象。
右侧的饼图显示当前快照中最大的对象。单机工具栏上的柱状图,可以查看当前堆的类信息,包括类的对象水量、浅堆(shallow heap)、深堆(ratained heap)
浅堆表示一个对象结构所占用内存的大小。
深堆表示一个对象被回收后,可以真实释放的内存大小
1)支配树(The Dominator Tree)
列出了堆中最大的对象,第二层级的节点表示当被第一层级的节点所引用到的对象,当第一层级对象被回收时,这些对象也将被回收。
这个工具可以帮助我们定位对象间的引用情况,垃圾回收时候的引用依赖关系
2)Path to GC Roots
被JVM持有的对象,如当前运行的线程对象,被systemclass loader加载的对象被称为GC Roots,从一个对象到GC Roots的引用链被称为Path to GC Roots。
通过分析Path to GC Roots可以找出JAVA的内存泄漏问题,当程序不在访问该对象时仍存在到该对象的引用路径