linux 服务器线上问题故障排查cpu、内存、磁盘、网络【经典版】

本文介绍了Linux服务器线上故障排查,重点关注CPU、磁盘、内存和网络四个方面。针对磁盘,使用df -hl、iostat进行分析;CPU排查涉及top、ps和jstack命令;内存排查包括堆内存、上下文切换,通过jmap、jstat等工具进行;网络排查则关注超时问题和TCP队列溢出。
摘要由CSDN通过智能技术生成

目录

一 线上故障排查概述

1.1 概述

二  磁盘的排查

2.1 磁盘排查

2.1.1 查看磁盘使用情况

2.1.2 查看磁盘读写性能

2.1.3 iotop命令定位文件读写来源

三  cpu的排查 

3.1. cpu的排查

3.2  分析阶段

3.2.1 使用jstack分析堆栈信息

3.2.1.1 案例1: 查看是否有waiting和timed_waiting等信息

3.2.1.2 案例2: 查看是否有“VM Thread” 虚拟机GC回收线程

3.2.1.3 案例3: 查看是否有“deadlock”关键字的信息

3.2.2 gc问题排查*

3.2.2.1 案例1:gc是否太频繁

3.2.2.2 案例2:jmap -heap 是否存在堆内存要溢出

3.2.2.3 案例3:使用jstat 查看内存使用率

3.2.3 上下文切换:cs(context switch)一列则代表了上下文切换的次数

四  内存的排查

4.1 内存整体排查

4.2 堆内内存

4.2.1 OOM异常

4.2.2.1 案例1:java栈问题

4.2.2.2 案例2:Java heap space内存溢出

4.2.2.3 案例3:Meta space 元空间问题

4.2.2.4 案例4:statckoverflow异常

4.2.2 排查工具-jmap定位代码内存泄漏

4.3 堆外内存(了解)

4.3.1 根据进程查看内存占有情况

​编辑

4.3.2 增长缓慢情况

4.3.3 对外内存对象的使用情况

4.4 GC问题

4.4.1 youngGC过于频繁

4.4.2 youngGC耗时过长

4.3.3 触发Full Gc

 五  网络的排查

5.1 超时问题

5.2 TCP队列溢出

5.3  close_wait

5.4 RST包问题


一 线上故障排查概述

1.1 概述

线上故障排查一般从cpu,磁盘,内存,网络这4个方面入手;

二  磁盘的排查

2.1 磁盘排查

2.1.1 查看磁盘使用情况

1.使用 df -hl  命令来查看磁盘使用情况

2.1.2 查看磁盘读写性能

1.从读写性能排查:iostat -d -k -x命令来进行分析

最后一列%util可以看到每块磁盘写入的程度,而rrqpm/s以及wrqm/s分别表示读写速度,一般就能帮助定位到具体哪块磁盘出现问题了。

2.1.3 iotop命令定位文件读写来源

1.iotop命令来进行定位文件读写的来源

输入:iotop ,显示如下:

2.图中第一列的tid为线程id,这里为1818,需要转成pid(进程id),可以使用命令:

【readlink -f /proc/*/task/tid/../..  】查找对应的进程id;

readlink  -f   /proc/*task/1818/../..

如下图所示:这里可以看到通过线程的id 1818 找到进程id   1681 ;

3. 这里看到查找到的pid为1681.找到pid后,使用 cat /proc/pid/io  就可以看这个进程具体的读写情况。

4. 使用命令lsof,lsof -p pid  查看具体进程打开的文件列表 ;如: lsof  -p  1681

三  cpu的排查 

3.1. cpu的排查

1.获取使用cpu较高的进程id;使用ps命令或者top命令找到具体的使用较高的进程号

=》这里使用命令: top  

2.使用消耗较高的线程id:用top -H -p pid来找到cpu使用率比较高的一些线程;执行top -H -p pid,这个命令就能显示刚刚找到的进程的所有线程的资源消耗情况注意此时显示pid是线程的id。

=》这里使用命令: top -H -p  1

3.使用printf 命令将线程id转成16进制的nid,目的是在线程的堆栈信息中查找对应线程的信息。【因为线程堆栈中的nid是PID的16进制表示】

这里使用 【 printf '%x\n' pid】得到nid (这里的pid为线程id),pid的16进制号为nid; 如下图:

=》这里使用命令:    printf  '%x\n'  66        ==》  0x42

3.2  分析阶段

3.2.1 使用jstack分析堆栈信息

1.使用jstack 命令查看具体线程堆栈信息: jstack pid |grep 'nid' -C5 –color

=》这里使用命令:  jstack  1 | grep   ‘ox42’  -C5  -color

查看线程堆栈:【进程id为1,线程id为66,的16进制表示为0x42】

3.2.1.1 案例1: 查看是否有waiting和timed_waiting等信息

1.通过从上文:抓取到了找到了nid为0x42的堆栈信,我们更关注是WAITING和TIMED_WAITING的部分,BLOCKED 等关键信息。

2.可以通过命令:【cat jstack.log | grep "java.lang.Thread.State" | sort -nr | uniq -c】来对jstack的状态有一个整体的把握,如果WAITING之类的特别多,那么多半是有问题啦。

3. 某个线程由于某种原因而进入WAITING状态,此时该功能整体不可用,但是无法复现

每隔30s,jstack查询一下,对比一直停留在parking 导致的WAITING状态的线程。例如CountDownLatch倒计时器,使得相关线程等待->AQS->LockSupport.park()。

4.随机出现大量线程访问接口缓慢:出现是比较随机的;平时消耗的CPU不多,而且占用的内存也不高。 思路: 首先找到该接口,通过压测工具不断加大访问力度,大量线程将阻塞于该阻塞点。找到业务代码阻塞点,这里业务代码使用了TimeUnit.sleep()方法,使线程进入了TIMED_WAITING(期限等待)状态。

3.2.1.2 案例2: 查看是否有“VM Thread” 虚拟机GC回收线程

如果"VM Thread" os_prio=0 tid=0x00007f871806e000 nid=0xa runnable,第一个双引号圈起来的就是线程名,如果是“VM Thread”这就是虚拟机GC回收线程了

3.2.1.3 案例3: 查看是否有“deadlock”关键字的信息

如果文件中找出关键字:deadlock;说明程序存在死锁,由于锁使用不当,导致死锁。

3.2.2 gc问题排查*

3.2.2.1 案例1:gc是否太频繁

1.使用jstat -gcutil 进程号 统计间隔毫秒 统计次数(缺省代表一致统计),查看某进程GC持续变化情况,如果发现返回中FGC(老年代垃圾回收次数)很大且一直增大。说明Full GC次数非常多,并且次数在不断增加,可以确认是Full GC。

接下来找到具体原因:

1)如果是生成大量的对象,导致内存溢出,执行jmap -dump:format=b,file=filename 进程ID,导出某进程下内存heap输出到文件中。可以通过eclipse的mat工具查看内存中有哪些对象比较多,查看具体内存对象占用情况。

2)内存占用不高,但是Full GC次数还是比较多,此时可能是代码中手动调用 System.gc()导致GC次数过多,这可以通过添加 -XX:+DisableExplicitGC来禁用JVM对显示GC的响应。

2. 使用jstat -gc pid 1000命令来对gc分代变化情况进行观察。 确定下gc是不是太频繁

S0C:第一个幸存区的大小
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间

JVM之jstat命令_s0c s1c s0u s1u ec eu oc ou mc mu ccsc ccsu ygc yg-CSDN博客

1000表示采样间隔(ms),S0C/S1C、S0U/S1U、EC/EU、OC/OU、MC/MU分别代表两个Survivor区、Eden区、老年代、元数据区的容量和使用量。YGC/YGT、FGC/FGCT、GCT则代表YoungGc、FullGc的耗时和次数以及总耗时。如果看到gc比较频繁,再针对gc方面做进一步分析。

3.2.2.2 案例2:jmap -heap 是否存在堆内存要溢出

可以使用jmap -heap 进程ID查看一下进程的堆内从是不是要溢出了,特别是老年代内从使用情况一般是达到阈值(具体看垃圾回收器和启动时配置的阈值)就会进程Full GC。

MySQL 服务占用cpu 100%,如何排查问题?

3.2.2.3 案例3:使用jstat 查看内存使用率

(1)查看1154进程的gc状态,每2秒刷新一次:jstat -gcutil 1154 2000

(2)查看每次gc后内存使用率有没有降下来,有两种情况。

Case1:如果降下来了,但是很快又升上去了,说明有可能是QPS比较高,这时候需要扩容

Case2:如果没降下来,说明很有可能是内存泄漏导致的。

(3)针对case2:

先用jmap命令看一下内存中对象的占用大小(jmap -histo:live pid),看能不能直接发现问题所在。

如果不能,就把dump文件下载下来,用分析软件进行分析(比如eclipse memory analyzer)。

(4)dump文件下载下来后,就要先让线上服务器可用,如果是刚上线了版本,就把服务器进行回滚;如果是很长时间没发版本,现在出现了问题,就重启服务
https://blog.csdn.net/xnninger/article/details/111499725

3.2.3 上下文切换:cs(context switch)一列则代表了上下文切换的次数

1.我们可以使用vmstat命令来进行查看, 频繁上下文问题,

2. 对特定的pid进行监控那么可以使用 pidstat -w pid命令 ,cswch和nvcswch表示自愿及非自愿切换。

四  内存的排查

4.1 内存整体排查

1.先用free命令先来检查一发内存的各种情况

4.2 堆内内存

内存问题大多还都是堆内内存问题。表象上主要分为OOM和StackOverflow

4.2.1 OOM异常

4.2.2.1 案例1:java栈问题

Jvm的内存不足时报:

Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread

原因:java栈的内存不足;

解决策略:

1.先从代码层面排查问题,如线程池忘记shutdown动作。

我们一般先会看下总体线程,通过pstreee -p pid |wc -l。

2.使用jstack或者jmap进行查看堆内存变化,

线程太多而且不被及时gc会引发unable to create new native thread

如果一切正常JVM方面可以通过指定Xss来减少单个thread stack的大小。

3.同时可以在【/etc/security/limits.conf】文件中,设置nofile(打开文件的最大数量)和nproc(用户可创建的进程的最大数量)来增大os对线程的限制。如下图:

4.2.2.2 案例2:Java heap space内存溢出

Jvm的内存不足时报:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

原因:这是最常见的OOM错误,原因是堆内存使用已经达到最大值-xmx。

解决策略:

1.先从代码层面排查问题

每次请求都new对象,导致大量重复创建对象;进行文件流操作未正确关闭;手动不当触发gc;ByteBuffer缓存分配不合理等都会造成代码OOM。

2.通过jstack和jmap去定位问题使用jstat来查看分代变化情况,比如youngGC或者fullGC次数是不是太多呀;EU、OU等指标增长是不是异常呀等。如果说一切都正常,才需要通过调整Xmx的值来扩大内存

4.2.2.3 案例3:Meta space 元空间问题

Jvm的内存不足时报:

Caused by: java.lang.OutOfMemoryError: Meta space

原因:元数据区的内存使用已经达到XX:MaxMetaspaceSize设置的最大值

解决策略:

1.先从代码层面排查问题

2.参数方面可以通过XX:MaxPermSize来进行调整(这里就不说1.8以前的永久代了)。

4.2.2.4 案例4:statckoverflow异常

栈内存溢出:

Exception in thread "main" java.lang.StackOverflowError

原因:线程栈需要的内存大于Xss值

解决策略:

1.先从代码层面排查问题

2.参数方面合理调整xss的大小,否则又报oom

4.2.2 排查工具-jmap定位代码内存泄漏

一般使用JMAPjmap -dump:format=b,file=filename pid来导出dump文件

使用Eclipse的Mat工具对dump文件进行分析:

1.内存泄漏问题一般我们直接选Leak Suspects即可,mat给出了内存泄漏的建议

2.选择Top Consumers来查看最大对象报告

3.和线程相关的问题可以选择thread overview进行分析

4.除此之外就是选择Histogram类概览来自己慢慢分析

5.在启动参数中指定-XX:+HeapDumpOnOutOfMemoryError来保存OOM时的dump文件。

4.3 堆外内存(了解)

Java虚拟机的堆以外的内存叫堆外内存(DirectBuffer),对于整个机器内存而言,除堆内内存以外部分即为堆外内存。堆外内存溢出表现就是物理常驻内存增长快。

4.3.1 根据进程查看内存占有情况

1.堆外内存溢出往往是和NIO的使用相关,一般我们先通过pmap来查看下进程占用的内存情况pmap -x pid | sort -rn -k3 | head -30,这段意思是查看对应pid倒序前30大的内存段。这边可以再过一段时间后再跑一次命令看看内存增长情况,或者和正常机器比较可疑的内存段在哪里。

我们如果确定有可疑的内存端,需要通过gdb来分析gdb --batch --pid {pid} -ex "dump memory filename.dump {内存起始地址} {内存起始地址+内存块大小}"

4.3.2 增长缓慢情况

1.一般对于堆外内存缓慢增长直到爆炸的情况来说,可以先设一个基线jcmd pid VM.native_memory baseline。

2.然后等放一段时间后再去看看内存增长的情况,通过jcmd pid VM.native_memory detail.diff(summary.diff)做一下summary或者detail级别的diff。

通过jcmd命令分析后,可以看到:堆内、线程以及gc等信息,

这里关注Internal的内存增长,如果增长十分明显的话那就是有问题了。

3.此外在系统层面,我们还可以使用strace命令来监控内存分配 strace -f -e "brk,mmap,munmap" -p pid

4.3.3 对外内存对象的使用情况

1.跟踪一下DirectByteBuffer对象的内存情况,通过jmap -histo:live pid手动触发fullGC来看看堆外内存有没有被回收。如果被回收了,那么大概率是堆外内存本身分配的太小了,通过-XX:MaxDirectMemorySize进行调整。如果没有什么变化,那就要使用jmap去分析那些不能被gc的对象,以及和DirectByteBuffer之间的引用关系了。

4.4 GC问题

使用jstat来获取当前GC分代变化信息。而更多时候,我们是通过GC日志来排查问题的,在启动参数中加上-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps来开启GC日志。

通过GC日志大致推断出youngGC与fullGC是否过于频繁或者耗时过长,从而对症下药。

这里选用G1垃圾收集器来做分析,这边也建议大家使用G1-XX:+UseG1GC

4.4.1 youngGC过于频繁

1.youngGC频繁一般是短周期小对象较多

A)是不是Eden区/新生代设置的太小了,

B)看能否通过调整-Xmn、-XX:SurvivorRatio等参数设置来解决问题。

C)如果参数正常,但是young gc频率还是太高,就需要使用Jmap和MAT对dump文件进行进一步排查了。

4.4.2 youngGC耗时过长

查看日志:确认gc耗时在那一块。以G1日志为例,可以关注Root Scanning、Object Copy、Ref Proc等阶段。

Ref Proc耗时长,就要注意引用相关的对象。

Root Scanning耗时长,就要注意线程数、跨代引用。Object Copy则需要关注对象生存周期。

比如说图中的Root Scanning和正常时间段,进行对比增长较多,那就是起的线程太多了

4.3.3 触发Full Gc

fullGC的原因可能包括以下这些,以及参数调整方面的一些思路:

1.程序主动执行System.gc():不要随便写就对了。

2.大对象分配失败:大对象找不到合适的region空间进行分配,就会进行fullGC,这种情况下可以增大内存或者增大-XX:G1HeapRegionSize。

3.晋升失败:在GC的时候没有足够的内存供存活/晋升对象使用,所以触发了Full GC。这时候可以通过-XX:G1ReservePercent来增加预留内存百分比,减少-XX:InitiatingHeapOccupancyPercent来提前启动标记,-XX:ConcGCThreads来增加标记线程数也是可以的。

4.并发阶段失败:在并发标记阶段,MixGC之前老年代就被填满了,那么这时候G1就会放弃标记周期。这种情况,可能就需要增加堆大小,或者调整并发标记线程数-XX:ConcGCThreads。

5.在启动参数中配置【-XX:HeapDumpPath=/xxx/dump.hprof】 来指定导出的dump文件存储;通过使用jinfo来进行gc前后的变化。

 常见JVM内存错误及解决方案 · 微服务架构 · 看云

 五  网络的排查

5.1 超时问题

读写超时。readTimeout/writeTimeout

连接超时。connectionTimeout

5.2 TCP队列溢出

执行netstat -s | egrep "listen|LISTEN" ,快速定位到tcp队列溢出

overflowed表示全连接队列溢出的次数,sockets dropped表示半连接队列溢出的次数。

5.3  close_wait

close_wait问题往往是由于某个地方阻塞住了,没有正常关闭连接,从而渐渐地消耗完所有的线程。想要定位这类问题,最好是通过jstack来分析线程堆栈来排查问题.

例如:开发同学说应用上线后CLOSE_WAIT就一直增多,直到挂掉为止,jstack后找到比较可疑的堆栈是大部分线程都卡在了countdownlatch.await方法发现异常仅仅是最简单的升级sdk后常出现的class not found。

JAVA 线上故障排查完整套路,从 CPU、磁盘、内存、网络、GC 一条龙!

5.4 RST包问题

使用tcpdump命令进行抓包,并使用wireshark进行简单分析了。

1. tcpdump -i en0 tcp -w xxx.cap,en0表示监听的网卡。

接下来我们通过wireshark打开抓到的包,可能就能看到如下图所示,红色的就表示RST包了 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值