CPU分析
当程序响应变慢的时候,首先使用top、vmstat、ps等命令查看系统的cpu使用率是否有异常,从而可以判断出是否是cpu繁忙造成的性能问题。
其中,主要通过us(用户进程所占的%)这个数据来看异常的进程信息。当us接近100%甚至更高时,可以确定是cpu繁忙造成的响应缓慢。一般说来,cpu繁忙的原因有以下几个:
-
线程中有无限空循环、无阻塞、正则匹配或者单纯的计算。
-
发生了频繁的gc : 通过jstat查看gc的log判断
-
多线程的上下文切换 : 通过vmstat中上下文切换(cs)次数来判断
确定好cpu使用率最高的进程之后就可以使用jstack来打印出异常进程的堆栈信息:
jstack [pid]
使用jstack只能打印出进程的信息,这些信息里面包含了此进程下面所有线程(轻量级进程-LWP)的堆栈信息。因此,进一步的需要确定是哪一个线程耗费了大量cpu,此时可以使用top -p [processId]来查看,也可以直接通过ps -Le来显示所有进程,包括LWP的资源耗费信息。
最后,通过在jstack的输出文件中查找对应的lwp的id即可以定位到相应的堆栈信息。其中需要注意的是线程的状态:RUNNABLE、WAITING等。对于Runnable的进程需要注意是否有耗费cpu的计算。对于Waiting的线程一般是锁的等待操作。
jstat -gcutil [pid]
也可以使用jstat来查看对应进程的gc信息,以判断是否是gc造成了cpu繁忙。
通过vmstat,通过观察内核状态的上下文切换(cs)次数,来判断是否是上下文切换造成的cpu繁忙。
内存分析
对Java应用来说,内存主要是由堆外内存和堆内内存组成。
1. 堆外内存堆外内存主要是JNI、Deflater/Inflater、DirectByteBuffer(nio中会用到)使用的。对于这种堆外内存的分析,还是需要先通过vmstat、sar、top、pidstat等查看swap和物理内存的消耗状况再做判断的。此外,对于JNI、Deflater这种调用可以通过Google-preftools来追踪资源使用状况。
2. 堆内内存此部分内存为Java应用主要的内存区域。通常与这部分内存性能相关的有:
-
创建的对象:这个是存储在堆中的,需要控制好对象的数量和大小,尤其是大的对象很容易进入老年代
-
全局集合:全局集合通常是生命周期比较长的,因此需要特别注意全局集合的使用
-
缓存:缓存选用的数据结构不同,会很大程序影响内存的大小和gc
-
ClassLoader:主要是动态加载类容易造成永久代内存不足
-
多线程:线程分配会占用本地内存,过多的线程也会造成内存不足
以上使用不当很容易造成:
-
频繁GC -> Stop the world,使你的应用响应变慢
-
OOM,直接造成内存溢出错误使得程序退出。OOM又可以分为以下几种:
-
Heap space:堆内存不足
-
PermGen space:永久代内存不足
-
Native thread:本地线程没有足够内存可分配
IO分析
通常与应用性能相关的包括:文件IO和网络IO。
1. 文件IO可以使用系统工具pidstat、iostat、vmstat来查看io的状况。
注意bi和bo这两个值,分别表示块设备每秒接收的块数量和块设备每秒发送的块数量,由此可以判定io繁忙状况。
进一步的可以通过使用strace工具定位对文件io的系统调用。通常,造成文件io性能差的原因不外乎:
-
大量的随机读写
-
设备慢
-
文件太大
2. 网络IO查看网络io状况,一般使用的是netstat工具。可以查看所有连接的状况、数目、端口信息等。例如:当time_wait或者close_wait连接过多时,会影响应用的相应速度。
还可以使用tcpdump来具体分析网络io的数据。当然,tcpdump出的文件直接打开是一堆二进制的数据,可以使用wireshark阅读具体的连接以及其中数据的内容。
还可以通过查看/proc/interrupts来获取当前系统使用的中断的情况。
https://mp.weixin.qq.com/s/bM1fLxs0b1BusN2YmfaaIQ