CPU - 使用jstack分析cpu问题
找出使用率最好进程id和最高线程id,看它的堆栈信息。
1、找出最高进程号。top 或 jps。如:12480
2、获取每个线程的cpu情况。
用top -H -p pid
来找到cpu使用率比较高的一些线程.如 top -H -p 12480 。 找到线程号12775.
3、将10进制线程号转16进制来查找堆栈信息
[admin@cr ~]$ printf '%x\n' 12775
31e7
4、执行 jstack <pid> | grep -A 10 <thread ox16 id> 。得到该线程堆栈所在行的最后10行信息。
例如:jstack 12480 | grep -A 10 31e7
jstack 12480 | grep -A 10 31e7 >> info.log 输出到文件中查看。
[admin@node0 logs]$ cat info.log | grep "java.lang.Thread.State" | sort -nr | uniq -c
来对jstack的状态有一个整体的把握,如果WAITING之类的特别多,那么多半是有问题啦。
5、查找日志排查存在问题代码。
频繁gc - jstat
使用jstat -gc pid 1000
命令来对gc分代变化情况进行观察,1000表示采样间隔(ms),S0C/S1C、S0U/S1U、EC/EU、OC/OU、MC/MU分别代表两个Survivor区、Eden区、老年代、元数据区的容量和使用量。YGC/YGT、FGC/FGCT、GCT则代表YoungGc、FullGc的耗时和次数以及总耗时。
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT CGC CGCT GCT
0.0 2048.0 0.0 2048.0 608256.0 63488.0 356352.0 120276.6 190032.0 182010.9 23040.0 20406.7 127 1.320 0 0.000 16 0.177 1.497
0.0 2048.0 0.0 2048.0 608256.0 63488.0 356352.0 120276.6 190032.0 182010.9 23040.0 20406.7 127 1.320 0 0.000 16 0.177 1.497
看gc日志分析案例
[ParNew: 1679677K->1878K(1887488K), 0.0176620 secs] 2204253K->526489K(6753536K), 0.0178770 secs] [Times: user=0.07 sys=0.00, real=0.02 secs]
[ParNew: 1679702K->2122K(1887488K), 0.0184380 secs] 2204313K->526767K(6753536K), 0.0186610 secs] [Times: user=0.06 sys=0.00, real=0.02 secs]
[ParNew: 1679946K->2104 K(1887488K), 0.0166490 secs] 2204591K->526796 K(6753536K), 0.0168640 secs] [Times: user=0.06 sys=0.00, real=0.01 secs]
[ParNew: 1679928 K->1646K(1887488K), 0.0174360 secs] 2204620K->526439 K(6753536K), 0.0176530 secs] [Times: user=0.06 sys=0.00, real=0.02 secs]
[GC(Young GC)2017-06-02T15:10:29.874+0800: 68770.091: [ParNew()使用ParNew作为年轻代的垃圾回收): 1679928 K(年轻代垃圾回收前的大小)->1646K年轻代垃圾回收以后的大小)(1887488K)(年轻代的总大小), 0.0174360 secs(回收时间)]] 2204620K(堆区垃圾回收前的大小)->526439K(堆区垃圾回收后的大小)(6753536K(堆区总大小), 0.0176530 secs(回收时间)] [Times: user=0.06Young GC用户耗时) sys=0.00(Young GC系统耗时), real=0.02 secsYoung GC实际耗时)]
从最后一条GC记录中我们可以看到 Young GC回收了 1679928-1646=1678282 K的内存
Heap区通过这次回收总共减少了 2204620-526439= 1678181 K的内存。
1678282-1678181=101K说明通过该次Young GC有101K的内存被移动到了Old Gen
我们来验证一下
在最后一次Young GC的回收以前 Old Gen的大小为 526796(倒数第二条堆内存)-2104=524692 <br/>
回收以后Old Gen的内存使用为 526439-1646=524793
Old Gen在该次Young GC以后内存增加了 524793-524692=101K 与预计的相符
Java应用频繁FullGC分析-阿里云开发者社区
上下文切换
针对频繁上下文问题,我们可以使用vmstat
命令来进行查看
vmstat pid
[admin@node0 ~]$ vmstat 2934
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 288692 77592 5330940 0 0 7 1078 0 0 2 1 97 1 0
cs(context switch)一列则代表了上下文切换的次数
如果我们希望对特定的pid进行监控那么可以使用 pidstat -w pid
命令,cswch和nvcswch表示自愿及非自愿切换。
磁盘
查看文件系统状态
[admin@node0 ~]$ df -hl
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 79G 68G 7.6G 90% /
iostat查看磁盘性能
[admin@node0 ~]$ iostat -d -k -x
Linux 3.10.0-1062.9.1.el7.x86_64 (node0) 02/11/2022 _x86_64_ (8 CPU)
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
vda 0.00 2.91 0.59 4.05 16.05 44.23 25.96 0.01 3.45 1.16 3.78 0.14 0.07
最后一列%util
可以看到每块磁盘写入的程度,而rrqpm/s
以及wrqm/s
分别表示读写速度,一般就能帮助定位到具体哪块磁盘出现问题了
lsof命令来确定具体的文件读写情况lsof -p pid
[admin@credit-dev-1 ~]$ lsof -p 23387
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
java 23387 admin cwd DIR 253,1 4096 1052949 /home/admin/deploy/flow-analysis
内存
1.先检查内存使用情况。free 主要包括OOM、GC问题和堆外内存。
堆内内存
内存问题大多还都是堆内内存问题。表象上主要分为OOM和StackOverflow。
OOM
JMV中的内存不足,OOM大致可以分为以下几种:
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
这个意思是没有足够的内存空间给线程分配java栈,基本上还是线程池代码写的有问题,比如说忘记shutdown,所以说应该首先从代码层面来寻找问题,使用jstack或者jmap。如果一切都正常,JVM方面可以通过指定Xss
来减少单个thread stack的大小。另外也可以在系统层面,可以通过修改/etc/security/limits.conf
nofile和nproc来增大os对线程的限制
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
这个意思是堆的内存占用已经达到-Xmx设置的最大值,应该是最常见的OOM错误了。解决思路仍然是先应该在代码中找,怀疑存在内存泄漏,通过jstack和jmap去定位问题。如果说一切都正常,才需要通过调整Xmx
的值来扩大内存。
Caused by: java.lang.OutOfMemoryError: Meta space
这个意思是元数据区的内存占用已经达到XX:MaxMetaspaceSize
设置的最大值,排查思路和上面的一致,参数方面可以通过XX:MaxPermSize
来进行调整
Stack Overflow
栈内存溢出,这个大家见到也比较多。
Exception in thread "main" java.lang.StackOverflowError
表示线程栈需要的内存大于Xss值,同样也是先进行排查,参数方面通过Xss
来调整,但调整的太大可能又会引起OOM。
使用JMAP定位代码内存泄漏
JMAP
jmap -dump:format=b,file=filename pid
来导出dump文件
工具地址 : Eclipse Memory Analyzer Open Source Project | The Eclipse Foundation
通过mat(Eclipse Memory Analysis Tools)导入dump文件进行分析,内存泄漏问题一般我们直接选Leak Suspects即可,mat给出了内存泄漏的建议。另外也可以选择Top Consumers来查看最大对象报告。和线程相关的问题可以选择thread overview进行分析。除此之外就是选择Histogram类概览来自己慢慢分析。
- Histogram可以列出内存中的对象,对象的个数以及大小。
- Dominator Tree可以列出那个线程,以及线程下面的那些对象占用的空间。
- Top consumers通过图形列出最大的object。
- Leak Suspects通过MA自动分析泄漏的原因。
另一方面,我们可以在启动参数中指定-XX:+HeapDumpOnOutOfMemoryError
来保存OOM时的dump文件。
gc问题和线程
gc问题除了影响cpu也会影响内存,排查思路也是一致的。一般先使用jstat来查看分代变化情况,比如youngGC或者fullGC次数是不是太多呀;EU、OU等指标增长是不是异常呀等。
线程的话太多而且不被及时gc也会引发oom,大部分就是之前说的unable to create new native thread
。除了jstack细细分析dump文件外,我们一般先会看下总体线程,
通过pstree -p pid |wc -l
。
[admin@node0 ~]$ pstree -p 17218 |wc -l
93
或者直接通过查看/proc/pid/task
的数量即为线程数量
[admin@node0 ~]$ ls -l /proc/17218/task | wc -l
95