性能优化与故障排查思路
性能优化的最佳位置是应用内部。
代码优化思路:
a.空间换时间:使用内存或者磁盘,换取更宝贵的CPU或者网络,如缓存的使用。
b.时间换空间:通过牺牲部分CPU.节省内存或者网络资源,如把一次大的网络传输变成多次。
c.其他,如并行化、异步化、池化操作。
CPU
1.CPU利用率高&&平均负载高
常见于CPU密集型,大量的线程处于运行状态,I/O较少。
排查思路:通过jstack多次打印线程栈,找到消耗CPU多的线程。 存在频繁的GC。
CPU利用率高也可能是CPU本身性能瓶颈。通过vmstat查看CPU利用率。
2.CPU利用率低&&平均负载高
常见于I/O密集型,(负载:R状态进程和D状态进程和)一般是在等待IO(磁盘IO和网络IO)。
排查思路:使用vmstat 1 定时输出系统资源使用,观察%wa(iowait)列的值,该列标识了磁盘I/O等待时间在CPU时间片中的百分比,如果超过30%,说明磁盘I/O等待严重,可能是大量磁盘随机访问或者直接的磁盘访问导致(没有使用缓存)。 结合iostat和dstat来验证。
此外,耗时较长的网络请求也会导致CPU的平均负载升高,如MYSQL慢查询、使用RPC接口获取接口数据等。这种情况的排查一般需要结合应用本身的上下游依赖关系以及中间件埋点的trace日志,进行综合分析。
3.CPU上下文切换次数变高
先用vmstat查看系统的上下文切换次数,然后通过pidstat观察进程的自愿上下文切换(cswch)和非自愿上下文切换(nvcswch)情况。自愿上下文切换,是因为应用内部线程状态发生转换所致,譬如调用sleep()、join()、wait()等,或者lock()、synchronized锁结构; 非自愿上下文切换,是因为线程由于被分配的时间片用完或由于执行优先级被调度器调度所致。
如果自愿上下文切换次数较高,意味着CPU存在自愿获取等待,比如说IO、内存等系统资源不足。如果是非自愿上下文切换次数高,可能的原因是应用内线程数过多,导致CPU时间片竞争激烈,频频被系统强制调度,此时可以结合jstack统计的线程数和线程状态分布看。
内存
内存分为系统内存和进程内存,我们一般遇到的是进程内存问题。
1.系统内存不足
Java应用一般都有单机或者集群的内存水位监控。一般系统内存不足,大概率是Java应用引起的。
使用Top命令可以看到Java应用进程的实际内存占用,其中RES标识进程的常驻内存使用,VIRT表示进程的虚拟内存占用,内存大小关系为:VIRT>RES>Java应用实际使用的堆大小。 Java应用内存占用=Heap(堆区)+Code Cache(代码缓存区)+Metaspace(元空间)+Symbol tables(符号表)+Thread stacks(线程栈区)+Direct buffers(堆外内存)+JVM(其他一些JVM自身占用)+Mapped files(内存映射文件)……
排查思路:a.先使用free查看当前内存的可用空间大小,然后使用vmstat查看具体的内存使用情况及内存增长趋势,找到占用内存最多的进程;b.分析缓存/缓冲区的内存使用,如果这个数值在一段时间变化不大,可以忽略。如果观察到缓存/缓冲区的大小在持续升高,则可以使用pcstat、cachetop、slabtop等工具,分析缓存/缓冲区的具体占用; c.排除缓存/缓冲区对系统内存的影响后,如果发现内存还在不断增长,说明可能存在内存泄漏。
2.Java内存溢出
3.Java内存泄漏
4.垃圾回收相关(GC)
磁盘IO和网络IO
1.磁盘IO问题排查思路:
a.使用工具输出磁盘相关的指标。常用的有%wa(iowait)、%util,根据输出判断磁盘IO是否存在异常,譬如%util这个指标高,说明有较重的IO行为;
b.使用pidstat定位到具体进程,关注下读或写的数据大小和速率;
c.使用lsof+进程号,查看该异常进程打开的文件列表,结合业务代码定位到IO来源。
注:%wa(iowait)升高不代表就一定意味着磁盘IO存在瓶颈,这个数值代表CPU上IO操作的时间占用的百分比,如果应用进程的这段时间内主要活动就是IO,那也正常。
2.网络IO瓶颈
a.一次传输的对象过大,可能会导致请求响应慢,同事GC频繁;
b.网络IO模型选择不合理,导致应用整体QPS较低,响应时间长;
c.RPC调用的线程池设置不合理。可用jstack统计线程数的分布,如果处于TIMED_WAITING或WAITING状态的线程较多,则需要重点关注。举例:数据库连接池不够用,体现在线程栈上就是很多线程在竞争一把连接池的锁;
d.RPC调用超时时间设置不合理,造成请求失败较多;
PS:前端优化,架构优化(分布式,缓存使用)、数据存储优化、代码优化