JVM 命令/工具使用介绍

JDK 自带了非常多的工具用于管理和监控 Java 应用程序状态,对于 Java 开发者来说,了解这些工具如何使用是非常有必要的,尤其是在排查线上问题,或者使用内部网络的情况下,可能无法或不允许使用三方工具,如 arthasjvm-tools 等,这个时候熟练使用 JDK 自带的 jvm 工具就非常有助于我们分析和解决 jvm 的问题。接下来我将对 JDK 中自带的比较常用的工具进行介绍,同时也会穿插部分其他 jvm 分析工具的讲解。

注意:以下内容均基于 JDK-11.0.12 版本讲解,不同版本的 JDK 中,命令或许有所不同。

jps:虚拟机进程状况工具

JPS(Java Virtual Machine Process Status Tool)是Java自带的命令行工具,用于列出当前运行的Java虚拟机(JVM)进程的信息。它提供了一种简单的方法来查看正在运行的Java进程的PID(Process ID)和主类名称。
以下是JPS命令的一般语法和选项:

1
jps [ options ]

常见的选项包括:

  • -q:只输出PID,不显示主类的名称。
  • -l:输出PID和主类的全名,即全限定类名。
  • -m:输出PID和主类的名称,并显示传递给main()方法的参数。
  • -v: 输出传递给 JVM 的参数

使用JPS命令可以获得以下信息:

  • 运行的Java进程的PID(Process ID):JPS可以列出当前正在运行的Java进程的PID,可以方便地使用其他工具(如jstack、jmap)进行进程诊断和分析。
  • 主类的名称:JPS可以显示启动Java进程的主类的名称,这对于排查运行中的Java进程非常有用。
  • 主类的参数:如果使用 -m 选项,JPS也可以显示传递给启动Java进程的主类的参数。这对于诊断和分析特定Java进程的配置和行为非常有用。

请注意,JPS只能检测与当前用户相关的Java进程。如果您想查看其他用户的Java进程,您可能需要使用root权限或使用适当的权限来运行JPS命令。

使用示例:

1
jps

image.png

1
jps -q

image.png

1
jps -l

image.png

1
jps -m

image.png

1
jps -v

image.png

jstack: 堆栈跟踪工具

jstack 用于生成Java进程的线程转储(Thread Dump),以便分析和诊断Java应用程序的线程状态和问题。线程转储是指记录了Java进程中各个线程的堆栈信息,可以用来分析线程的运行状态、死锁、死循环等问题。

jstack 的一般语法如下:

1
jstack [-l][-e] <pid>

常见的选项包括:

  • -l: 以长格式输出线程转储信息。此参数可以显示关于每个线程的附加信息,如锁信息、监视器信息等
  • -e: 生成线程转储时输出扩展信息。它会显示更详细的线程堆栈信息,包括与线程关联的本地方法(Native Method)的符号信息

使用 jstack 命令可以获得以下信息:

  • 线程堆栈信息:jstack 会打印出Java进程中每个线程的堆栈跟踪信息(包括正在运行的线程和等待中的线程),其中包含了线程调用栈的方法、行号和类信息。
  • 死锁信息:通过查看线程转储,可以检测并诊断可能存在的死锁情况。死锁是指多个线程互相持有对方需要的资源而导致的相互等待的情况。
  • 监控线程状态:线程转储还可以帮助监控线程状态,例如查找长时间运行的线程、查找线程的等待和阻塞情况等。

jstack 命令可以用于在线上或开发环境中定位和分析Java程序中的线程问题,如死锁、CPU高占用等。通常结合其他工具,如jps、jconsole、VisualVM等一起使用,以获取更全面的线程和性能信息,从而进行诊断和分析。

使用示例:

1
2
# 使用刚才 springboot-demo-0.0.1-SNAPSHOT.jar 的进程来分析堆栈
jps | grep demo | awk '{print $1}' | xargs -n 1 jstack -l > jstack.log

image.png

我们从 jstack.log 中截取一段内容,来看下这段内容表示什么意思

1
2
3
4
5
6
7
8
"Reference Handler" #2 daemon prio=10 os_prio=31 cpu=1.92ms elapsed=1359.35s tid=0x00007ff67900b800 nid=0x4403 waiting on condition  [0x000070000e0a5000]
java.lang.Thread.State: RUNNABLE
at java.lang.ref.Reference.waitForReferencePendingList(java.base@11.0.12/Native Method)
at java.lang.ref.Reference.processPendingReferences(java.base@11.0.12/Reference.java:241)
at java.lang.ref.Reference$ReferenceHandler.run(java.base@11.0.12/Reference.java:213)

Locked ownable synchronizers:
- None

“Reference Handler” #2 daemon prio=10 os_prio=31 cpu=1.92ms elapsed=1359.35s tid=0x00007ff67900b800 nid=0x4403 waiting on condition [0x000070000e0a5000]:

  • “Reference Handler” 是线程的名称。
  • #2 表示该线程的序号。
  • daemon 表示该线程是一个守护线程。
  • prio=10 表示该线程的调度优先级。
  • os_prio=31 是操作系统对该线程的优先级。
  • cpu=1.92ms 表示该线程占用 CPU 的时间。
  • elapsed=1359.35s 表示该线程的运行时间。
  • tid=0x00007ff67900b800 是线程的十六进制表示的线程 ID。
  • nid=0x4403 是线程的十六进制表示的线程 ID,nid 也就是 Native ID。
  • waiting on condition 表示该线程当前正在等待某个条件。
  • [0x000070000e0a5000] 是线程的十六进制表示的栈地址。

java.lang.Thread.State: RUNNABLE:

  • java.lang.Thread.State 表示线程的状态。
  • RUNNABLE 表示线程正在运行。

jstack 命令获取到的线程状态主要有以下几种:

  • NEW:线程已创建但尚未启动。
  • RUNNABLE:线程正在执行或准备执行,可以在操作系统的可执行队列中运行。
  • BLOCKED:线程正在等待获得一个监视器锁(synchronized关键字)以进入同步代码块。
  • WAITING:线程正在无限期地等待另一个线程采取某些操作,直到被中断或唤醒。
  • TIMED_WAITING:线程正在等待另一个线程采取某些操作,但等待的时间有限,可以是超时等待或等待指定的条件。
  • TERMINATED:线程已完成执行,结束了它的生命周期。

at java.lang.ref.Reference.waitForReferencePendingList(java.base@11.0.12/Native Method):

  • 这是线程调用栈的一部分,显示了线程正在执行的方法。
  • at 后面是方法的完整名称。

Locked ownable synchronizers::

  • 这是线程拥有的独占锁的列表。

-None:

  • 表示线程当前未拥有任何独占锁。

除了直接查看 jstack 生成的原始文件外,也可以借助其他工具来辅助我们分析,以下是几种常用的分析工具:

———————— 昏割线,太困了,先睡了 ————————