Java排查JVM问题(进程反应缓慢、僵死、CPU 占用过高、内存等问题)
目录
公号:码农充电站pro
1,ps 找到消耗资源最高的线程 ID
先用 ps 命令找到 Java 进程ID:
ps -aux| grep ...
使用 top
命令查看某进程中的所有线程的资源使用情况:
top -Hp `进程id`
也可以使用如下命令:
ps H -eo pid,tid,%cpu| grep `进程id`
将要分析的线程ID转换成十六进制:
printf "%x\n" `线程ID`
2,jstack 打印线程栈信息
使用 jstack
打印 Java 进程中的线程栈信息:
jstack `进程ID`
要特别注意
:使用jstack
命令时的用户一定要与启动 Java 进程的用户一样,否则会出现如下错误:
9798: Unable to open socket file: target process not responding or HotSpot VM not loaded
The -F option can be used when the target process is not responding
jstack
输出正常的话,会输出下面一样的信息:
"Thread-34" #613 daemon prio=5 os_prio=0 tid=0x00007fb9b006f800 nid=0x13a9c waiting on condition [0x00007fb90acaa000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000006c10a8828> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1081)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
然后,从这些信息中查找某个线程 nid
的状态等信息。
参考资料:
3,JDK 内置命令行工具
命令 | 含义 |
---|---|
jps/jinfo | 查看 java 进程 |
jstat | 查看 JVM 内部 gc 相关信息 |
jmap | 查看 heap 或类占用空间统计 |
jstack | 查看线程信息 |
jcmd | 执行 JVM 相关分析命令 |
4,Java 内存问题排查
一般 Java 内存问题可以通过生成 HeapDump (堆转储)文件来分析,它是一个Java进程在某个时间点上的内存快照。
堆转储文件是诊断内存相关问题的重要信息来源,例如内存泄漏,垃圾收集问题 和 java.lang.OutOfMemoryError,同时它也是优化内存消耗的重要依据。
生成堆转储文件有多种方式:
- jmap:在服务器上使用该方式比较合适
- 格式:
jmap -dump:format=b,file=<file-path> <pid>
- 示例:
jmap -dump:format=b,file=/opt/tmp/dumpfile.hprof 87960
- 格式:
- jvisualvm:可视化监控工具
jvisualvm 可以监控 Java 的CPU、堆、类、线程等指标:
要想使用 jvisualvm
或 jconsole
连接,需要先在服务端(Tomcat)开启 jmx 服务,在 Tomcat/bin/setenv.sh
文件中加入:
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote"
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.ssl=false"
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.authenticate=false"
JAVA_OPTS="$JAVA_OPTS -Djava.rmi.server.hostname=192.168.0.222" # 服务器IP
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.port=12345" # 端口
然后需要打开防火墙端口:
firewall-cmd --add-port=12345/tcp --permanent
firewall-cmd --reload
除了打开上面的端口外,还需要打开两个端口,使用 grep
查找:
# 先找到 Tomcat 进程 id
netstat -npl| grep 12345
tcp6 0 0 :::12345 :::* LISTEN 25613/java
# tomcat pid 为 25613
# 再找出其它两个端口
netstat -npl| grep 25613
tcp6 0 0 :::43853 :::* LISTEN 25613/java # 这个
tcp6 0 0 :::12345 :::* LISTEN 25613/java
tcp6 0 0 :::45985 :::* LISTEN 25613/java # 这个
# 将 43853 和 45985 端口也开放
firewall-cmd --add-port=43853/tcp --permanent
firewall-cmd --add-port=45985/tcp --permanent
firewall-cmd --reload
# 查看开放的端口
firewall-cmd --list-ports
生成堆dump 文件后,使用 MemoryAnalyzer 来分析。
MemoryAnalyzer 下载页面:
软件版本解压后目录内有个MemoryAnalyzer.ini文件,该文件里面有个 Xmx 参数,该参数表示最大内存占用量,默认为1024m,根据堆转储文件大小修改该参数即可。
- MemoryAnalyzer.ini中的参数一般默认为 -vmargs– Xmx1024m,这就够用了。假如你机器的内存不大,改大该参数的值,会导致MemoryAnalyzer启动时,报错:Failed to create the Java Virtual Machine。
- 当你导出的dump文件的大小大于你配置的1024m(说明1中,提到的配置:-vmargs– Xmx1024m),MAT输出分析报告的时候,会报错:An internal error occurred during: “Parsing heap dump from XXX”。适当调大说明1中的参数即可。
分析示例:
5,Linux 其它监控工具
1,vmstat 命令
vmstat 是一款指定采样周期和次数的功能性监测工具,它不仅可以统计内存的使用情况,还可以观测到 CPU 的使用率、swap 的使用情况。
各项参数的含义:
- r:等待运行的进程数
- b:处于非中断睡眠状态的进程数
- swpd:虚拟内存使用情况
- free:空闲的内存
- buff:用来作为缓冲的内存数
- si:从磁盘交换到内存的交换页数量
- so:从内存交换到磁盘的交换页数量
- bi:发送到块设备的块数
- bo:从块设备接收到的块数
- in:每秒中断数
- cs:每秒上下文切换次数
- us:用户 CPU 使用时间
- sy:内核 CPU 系统使用时间
- id:空闲时间
- wa:等待 I/O 时间
- st:运行虚拟机窃取的时间
2,阿里 Arthas
文章作者 @码农加油站
上次更改 2022-03-28