线上故障主要会包括cpu、磁盘、内存以及网络问题,而大多数故障可能会包含不止一个层面的问题,所以进行排查时候尽量四个方面依次排查一遍。同时例如jstack、jmap等工具也是不囿于一个方面的问题的,基本上出问题就是df、free、top 三连,然后依次jstack、jmap伺候,具体问题具体分析即可。
文章目录
- CPU 篇
- 查看实时线程堆栈信息
- 对jstack文件进行分析
- 频繁GC
- 上下文切换
- 内存篇
- 如何分析Dump文件内容
- youngGC 过于频繁
- youngGC 耗时过长
- 磁盘篇
- 网络篇
- netstat
- 附录
- jstack 线程快照
- jstat 虚拟机运行时统计
- jmap内存快照
- jinfo 虚拟机配置参数
- netstat
- 文本分析命令
CPU 篇
原因包括:业务逻辑问题(一般是死循环)、频繁gc以及上下文切换过多,可使用jstack分析线程对应的堆栈情况
查看实时线程堆栈信息
top
命令查找cpu使用率较高的进程- 接着用
top -Hp pid
找到cpu使用率较高的线程 printf '%x\n' pid
转换为nid- 使用
jstack pid | grep 'nid' -A10 --color
找到对应的堆栈信息 (pid为进程pid)
(参考)
最佳实践步骤
# 先将线程占用快照信息输出到快照文件
top -H -n 1 -p 17810 > 17810.jstack
# 再将jstack信息也保存到快照文件
jstack -l 17810 >> 17810.jstack
# 然后进行分析
printf '%x\n' 18124
cat 17810.jstack | grep '46cc' -A20 --color
对jstack文件进行分析
通常我们更专注 WATING 和TIMED_WATING 的线程,BLOCKED更不用多说了
- 将jstack信息输出到log文件中
jstack -l pid >> pid.jstack.log
cat jstack.log | grep "java.lang.Thread.State" | sort -nr | uniq -c
对jstack状态有一个整体的把握,如果WAITING之类的特别多,多半有问题
cat jstack.log | grep -i -E 'BLOCKED | deadlock'
观察处于BOLCKED状态的线程数量,以及死锁信息
频繁GC
jstat -gc pid 1000
对gc分代变化情况进行观察,1000表示采样间隔(ms)
S0C/S1C | S0U/S1U | EC/EU | OC/OU | MC/MU |
---|---|---|---|---|
S0/S1 容量(kb) | S0/S1 使用量(kb) | Eden区容量/使用量(kb) | 老年代容量/使用量(kb) | 元数据区容量/使用量(kb) |
次数代表从应用程序启动到采样时的次数
YGC/YGT | FGC/FGCT | GCT |
---|---|---|
YoungGC 次数/耗时(s) | FullGC 次数/耗时(s) | 总耗时(s) |
上下文切换
vmstat pid
命令 cs(context switch) 代表上下文切换次数
pidstat -w pid
命令 cswch 自愿切换/nvcswch 非自愿切换
内存篇
导出dump文件 jmap -dump:format=b,file=filename.hprof pid
, 分析工具可使用 通过mat(Eclipse Memory Analysis Tools)导入dump文件进行分析 、jprofiler
如何分析Dump文件内容
分析dump文件,可以使用idea简单分析,比较方便实用,得到dump文件后
通过Run -> OpenProfiler Snapshot -> Open 路径打开 dump文件,例如 xxxx.hprof
打开后可以看到内存快照中哪些对象数量过多,占用内存大小等信息,
这里我们选中左侧byte[] 双击,看到如下内容
关注GC root 信息,大致就能直接定位到源码位置所在,也就大致分析出来问题故障点,当然实用JProfiler可以得到更加详细的信息,故障点位置等,这里不展开,可以评论区讨论
youngGC 过于频繁
短周期小对象过多,考虑Eden区/新生代是否设置过小,调整 -Xmn、-XX:SurvivorRatio ,频率仍然高,可以分析dump文件
youngGC 耗时过长
G1日志、jinfo
磁盘篇
df -hl 查看文件系统状态
磁盘性能分析通过 iostatiostat -d -k -x
,最后一列%util可以看到每块磁盘写入的程度,而rrqpm/s以及wrqm/s分别表示读写速度,一般就能帮助定位到具体哪块磁盘出现问题
iotop
命令来进行定位文件读写的来源,不过这边拿到的是tid,我们要转换成pid,可以通过readlink来找到pidreadlink -f /proc/*/task/tid/…/…
网络篇
netstat
netstat -tlnp
查看系统中的TCP连接监听地址、端口以及进程等信息
查看端口8080被哪个进程占用:netstat -tlpn | grep :8080
查看进程1000使用了哪些tcp端口:netstat -tlpn | grep 1000
netstat -tn | grep -v ESTABLISHED
查看系统中的非正常连接
附录
jstack 线程快照
jstack是Java开发工具包(JDK)中的一个命令行工具,用于生成Java虚拟机(JVM)当前时刻的线程快照(Thread Dump)。线程快照可以用于分析和调试Java应用程序中的线程问题,如死锁、线程等待等。
jstack命令的常见参数如下:
-
-l
:除了打印线程堆栈信息外,还会额外打印关于锁的附加信息,如拥有该锁的线程等待的条件。 -
-F
:当正常情况下无法获得线程快照时,强制生成线程快照。这通常在JVM处于死锁或长时间无响应状态时使用。 -
-m
:除了打印线程堆栈信息外,还会打印与线程关联的本地方法堆栈信息。 -
-h
或-help
:打印jstack命令的帮助信息,包括所有可用的参数和选项。
常见的jstack用法如下:
-
打印Java进程的线程快照:
jstack <pid>
<pid>
是Java进程的进程ID。 -
打印Java进程的线程快照,并将结果保存到文件中:
jstack <pid> > thread_dump.txt
将线程快照输出重定向到文件
thread_dump.txt
中,以便后续分析和查看。 -
使用特定的参数生成线程快照:
jstack -l -F <pid>
这个命令将生成带有附加锁信息的强制线程快照。
-
生成线程快照并指定输出到标准错误流(stderr):
jstack -l <pid> 2> error.log
这个命令将线程快照输出到标准错误流,并将其重定向到文件
error.log
中。 -
使用jstack命令连接到远程Java进程并生成线程快照:
jstack -l <hostname>:<port>
<hostname>
是远程主机的名称或IP地址,<port>
是远程Java进程的调试端口。
jstack命令是诊断Java应用程序中线程问题的有用工具,可用于定位性能问题、死锁和线程等待等情况。通过分析线程快照,可以获得关于线程状态、锁的信息以及线程之间的相互关系,帮助开发人员进行故障排除和性能优化。
关于java.lang.Thread.State
要根据jstack
命令输出的内容中的java.lang.Thread.State
来判断系统的总体运行情况,可以关注线程的状态并进行分析。java.lang.Thread.State
表示线程的状态,可以提供有关线程活动和阻塞的信息。以下是一些常见的线程状态和它们的含义:
-
RUNNABLE
(可运行状态):线程正在执行,或者等待CPU资源执行。 -
BLOCKED
(阻塞状态):线程正在等待获取锁,而其他线程已经持有了该锁。 -
WAITING
(等待状态):线程正在等待另一个线程发出特定信号,直到其他线程通知它继续执行。 -
TIMED_WAITING
(计时等待状态):线程正在等待一段时间,直到超时或者其他线程通知它继续执行。 -
TERMINATED
(终止状态):线程已经执行完毕或者因为异常等原因被终止。
通过观察jstack
命令输出中各个线程的状态,可以对系统的总体运行情况进行初步判断。下面是一些常见的判断场景:
-
如果大多数线程处于
RUNNABLE
状态,系统可能正常运行,并且没有明显的性能瓶颈。 -
如果有大量线程处于
BLOCKED
状态,系统可能存在严重的竞争条件或死锁情况。需要进一步分析具体的线程堆栈信息来解决问题。 -
如果有线程处于
WAITING
或TIMED_WAITING
状态,可以根据线程等待的对象或条件来判断是否有资源争用或线程间通信的问题。 -
如果有线程处于
TERMINATED
状态,表示线程已经执行完毕,但可以进一步观察是否有异常抛出或其他异常情况发生。
需要注意的是,仅通过线程状态无法完全确定系统的总体运行情况,因为线程状态只是系统的一个方面。综合考虑线程状态、线程数量、CPU使用率、内存使用情况等信息可以更全面地评估系统的运行状况。在分析线程堆栈信息时,还应注意查看是否存在异常、死锁、阻塞等特定的线程问题,并进行相应的故障排除和性能优化。
jstat 虚拟机运行时统计
jstat是Java开发工具包(JDK)中的一个命令行工具,用于监视和收集Java虚拟机(JVM)的各种运行时统计信息。它可以提供有关堆内存、垃圾回收、类加载、线程和编译器等方面的数据,用于性能分析和故障排除。
jstat命令的常见参数如下:
-
-class
:打印类加载、卸载和总计的统计信息。 -
-compiler
:打印JIT编译器的统计信息,包括编译任务数量、编译耗时等。 -
-gc
:打印垃圾回收统计信息,包括堆内存使用情况、垃圾回收次数、回收时间等。 -
-gccapacity
:打印堆内存容量和使用情况的详细信息,包括新生代、老年代、持久代的容量、使用量和空闲量等。 -
-gcutil
:打印垃圾回收统计信息的摘要,包括垃圾回收器的使用率、堆内存的使用率等。 -
-printcompilation
:打印JIT编译器编译方法的详细信息,包括方法名称、编译状态、编译耗时等。 -
-utilization
:打印垃圾回收器的利用率,包括新生代、老年代、元数据区的利用率。 -
-options
:打印JVM的命令行选项和系统属性。
常见的jstat用法如下:
-
监视Java进程的类加载情况:
jstat -class <pid> [interval] [count]
<pid>
是Java进程的进程ID,[interval]
是采样间隔时间(以毫秒为单位,默认值为1000ms),[count]
是采样次数(默认值为无限次)。 -
监视Java进程的垃圾回收统计信息:
jstat -gc <pid> [interval] [count]
<pid>
是Java进程的进程ID,[interval]
是采样间隔时间(以毫秒为单位,默认值为1000ms),[count]
是采样次数(默认值为无限次)。 -
监视Java进程的堆内存容量和使用情况:
jstat -gccapacity <pid> [interval] [count]
<pid>
是Java进程的进程ID,[interval]
是采样间隔时间(以毫秒为单位,默认值为1000ms),[count]
是采样次数(默认值为无限次)。 -
监视Java进程的JIT编译器统计信息:
jstat -compiler <pid> [interval
jmap内存快照
jmap是Java开发工具包(JDK)中的一个命令行工具,用于生成Java进程的内存映像和堆转储快照。它可以用于分析Java应用程序的内存使用情况、查找内存泄漏和性能问题等。
jmap命令的常见参数如下:
-
-heap
:打印Java进程的堆内存使用情况,包括堆的大小、使用量、垃圾回收器的详细信息等。 -
-histo
:打印Java进程的堆内存中对象的统计信息,包括对象类型、数量和占用内存大小等。 -
-dump:<dump-options>
:生成Java进程的堆转储快照(Heap Dump),可以用于分析内存泄漏和对象分布等。<dump-options>
可以是以下选项之一:live
:只转储活动对象(默认选项)。all
:转储所有对象,包括活动对象和垃圾对象。file=<filename.hprof>
:将堆转储快照保存到指定的文件中。format=<format>
:指定堆转储快照的格式,可以是b
(二进制格式)或h
(文本格式,默认值)。
-
-finalizerinfo
:打印等待终结的对象的信息,包括等待终结队列的长度和具体对象的信息。 -
-permstat
:打印永久代(PermGen/Metaspace)的统计信息,包括类加载器、类和字符串的数量和占用内存大小等。
常见的jmap用法示例如下:
-
打印Java进程的堆内存使用情况:
jmap -heap <pid>
<pid>
是Java进程的进程ID。 -
打印Java进程的堆内存中对象的统计信息:
jmap -histo <pid>
<pid>
是Java进程的进程ID。 -
生成Java进程的堆转储快照,并保存到文件中:
jmap -dump:file=<filename> <pid>
<filename>
是要保存堆转储快照的文件名,<pid>
是Java进程的进程ID。 -
打印等待终结的对象的信息:
jmap -finalizerinfo <pid>
<pid>
是Java进程的进程ID。 -
打印永久代(PermGen/Metaspace)的统计信息:
jmap -permstat <pid>
<pid>
是Java进程的进程ID。
jmap命令是一个强大的工具,可以帮助开发人员分析Java应用程序的内存使用情况和性能问题。通过使用不同的参数组合,可以获取有关堆内存、对象统计、堆转储等方面的详细信息。请注意,在使用jmap命令时,建议谨慎使用并避免在生产环境中使用,以避免对应用程序性能产生负面影响。
jinfo 虚拟机配置参数
jinfo是Java开发工具包(JDK)中的一个命令行工具,用于查看和调整Java进程的Java虚拟机(JVM)配置参数和系统属性。它可以帮助开发人员获取Java进程的运行时信息,并对其进行动态调整。
jinfo命令的常见参数如下:
-
-flag <name>
:打印指定JVM参数的当前值和默认值。 -
-flags
:打印Java进程的所有JVM参数和系统属性。 -
-sysprops
:打印Java进程的所有系统属性。 -
-h
或-help
:打印jinfo命令的帮助信息,包括所有可用的参数和选项。
常见的jinfo用法示例如下:
-
打印Java进程的指定JVM参数的当前值和默认值:
jinfo -flag <name> <pid>
<name>
是要打印的JVM参数的名称,<pid>
是Java进程的进程ID。 -
打印Java进程的所有JVM参数和系统属性:
jinfo -flags <pid>
<pid>
是Java进程的进程ID。 -
打印Java进程的所有系统属性:
jinfo -sysprops <pid>
<pid>
是Java进程的进程ID。
jinfo命令可以帮助开发人员获取Java进程的运行时信息,如JVM参数和系统属性的值。通过查看这些信息,可以了解Java进程的配置和环境,帮助进行故障排除和性能优化。此外,jinfo还可以用于动态调整JVM参数的值,但在使用时需要谨慎,避免对应用程序产生不可预知的影响。
netstat
netstat命令的一些常见用法示例,以帮助排查端口被哪个进程占用等相关信息:
-
显示所有活动连接和监听端口:
netstat -a
-
仅显示TCP连接:
netstat -at
-
仅显示UDP连接:
netstat -au
-
显示与网络连接关联的进程/程序:
netstat -ap
-
显示正在监听的TCP端口:
netstat -ltn
-
显示正在监听的UDP端口:
netstat -lun
-
显示与指定端口相关的进程/程序:
netstat -tulpn | grep <端口号>
例如,要查找占用端口80的进程,可以使用:
netstat -tulpn | grep :80
-
显示网络统计信息和摘要:
netstat -s
-
显示路由表信息:
netstat -r
这些示例将帮助快速了解网络连接状态、监听端口和相关进程的信息。请注意,有些示例可能需要root或管理员权限才能显示完整的信息。
netstat命令还有其他参数和选项可用于更精细的控制和筛选输出。可以通过man netstat
命令在终端上查看完整的netstat命令手册以获取更多详细信息
文本分析命令
cat info.log | awk -F '(回调类型EventType=|,callbackMsg)' '{print $2}' | sort | uniq -c | sort -nr
从名为 info.log 的文件中读取内容,使用 awk 命令将每行按照指定的字段分隔符提取出第二个字段,然后对提取的字段进行排序,统计每个字段出现的次数,并按照次数进行逆序排列。
cat
将文本文件内容输出到标准输出
awk
是一种文本处理工具,它可以按行扫描和处理文件。
-F '(回调类型EventType=|,callbackMsg)'
用于指定字段分隔符,这里是使用正则表达式作为分隔符,表示字段可以通过字符串 “回调类型EventType=” 或 “,callbackMsg” 来分隔。
'{print $2}'
是一个 AWK 脚本,表示打印每行中的第二个字段。
uniq -c
用于去除重复的行,保留唯一的行 -c
用于计数并显示美航出现的次数
sort -nr
按照数值降序,从大到小