公号:码农充电站pro

主页:https://codeshellme.github.io

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、堆、类、线程等指标:

在这里插入图片描述


要想使用 jvisualvmjconsole 连接,需要先在服务端(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