Jcmd 虚拟机诊断利器
Java虚拟机(JVM)是运行Java程序的抽象化的计算器。今天,来学习下如何轻松诊断正在运行的JVM。
JDK本身中提供了许多可用的工具,可以用于各种开发、监视和故障排除活动。推荐使用jcmd,简单易懂,可以提供关于JVM运行的各种信息。此外,jcmd是JDK7以后的一个推荐工具,用于增强JVM诊断,性能开销低。
是什么
JVMCommand 简称jcmd, 是JDK自带的一个向JVM发送诊断命令请求的小工具,免费使用,位于${JAVA_HOME}/bin目录下。但是,它必须在运行JVM的同一台机器上使用。不能向其它工具提供远程诊断的功能,如 Jconsole。接下来看看如何在诊断JVM。
获取PID
在排查问题时,通常情况下,开发者/运维人员需要知道Java程序对用的PID,然后分析PID占用的资源情况。如IO、内存、CPU、线程状态等等。但是前提是:找到PID。JDK 或 linux系统提供了很多种方式给开发者排查正在运行的JVM(PID)
-
jps - Java Virtual Machine Process Status Tool, 访问主机JAVA进程的工具
AndydeMacBook-Pro:bin andy$ jps 82800 Jps 80180 Launcher
jps 命令不带参数时,只显示PID、主类名
# 只显示pid jps -q # 显示pid、类名及相关参数 jps -m # 显示pid、运行参数 jps -v
-
ps - 是linux提供的一个强大的工具,一般根据关键字进行过滤
ps -ef | grep java
以上的信息太过复杂,使用awk工具做筛选过滤
ps -ef | grep java | awk '{print $2}'
-
jcmd - 哈哈 今日主角登场,使用该命令,列出所有正在运行的JVM进程
类似 jps -m 命令的效果
获取进程参数选项
可以使用jcmd pid help 命令获取指定PID的参数选项列表,不同版本的JVM结果列表略有不同
AndydeMacBook-Pro:bin andy$ jcmd 80180 help
80180:
The following commands are available:
Compiler.CodeHeap_Analytics
Compiler.codecache
Compiler.codelist
Compiler.directives_add
Compiler.directives_clear
Compiler.directives_print
Compiler.directives_remove
Compiler.queue
GC.class_histogram
GC.finalizer_info
GC.heap_dump
GC.heap_info
GC.run
GC.run_finalization
JFR.check
JFR.configure
JFR.dump
JFR.start
JFR.stop
JVMTI.agent_load
JVMTI.data_dump
ManagementAgent.start
ManagementAgent.start_local
ManagementAgent.status
ManagementAgent.stop
Thread.print
VM.cds
VM.class_hierarchy
VM.classloader_stats
VM.classloaders
VM.command_line
VM.dynlibs
VM.events
VM.flags
VM.info
VM.log
VM.metaspace
VM.native_memory
VM.print_touched_methods
VM.set_flag
VM.stringtable
VM.symboltable
VM.system_properties
VM.systemdictionary
VM.uptime
VM.version
help
jcmd 常用选项
上述罗列的参数选项非常的多,不做一一介绍。来学习下工作中经常使用的参数选项。
VM.version
AndydeMacBook-Pro:bin andy$ jcmd 80180 VM.version
80180:
Java HotSpot(TM) 64-Bit Server VM version 17.0.5+9-LTS-191
JDK 17.0.5
获取JVM的基本信息
VM.system_properties
获取JVM运行的系统参数,由于篇幅过长,做了一些删减
AndydeMacBook-Pro:bin andy$ jcmd 80180 VM.system_properties
80180:
#Tue Dec 06 10:36:15 CST 2022
http.proxyHost=127.0.0.1
java.specification.version=17
sun.jnu.encoding=UTF-8
java.class.path=/Applications/IntelliJ IDEA CE.app/Contents/plugins/java/lib/jps-launcher.jar
https.proxyPort=50154
java.vm.vendor=Oracle Corporation
jdt.compiler.useSingleThread=true
sun.arch.data.model=64
kotlin.incremental.compilation=true
kotlin.daemon.client.alive.path="/var/folders/z8/4hs2q4zx73q6zfg2myh6m_kh0000gn/T/kotlin-idea-7350919135759315545-is-running"
java.vendor.url=https\://java.oracle.com/
user.timezone=Asia/Shanghai
...
VM.flags
输出所有使用的VM参数,这些参数要么由开发指定,要么由JVM默认使用:
AndydeMacBook-Pro:bin andy$ jcmd 80180 VM.flags
80180:
-XX:CICompilerCount=4 -XX:ConcGCThreads=2 -XX:G1ConcRefinementThreads=8 -XX:G1EagerReclaimRemSetThreshold=8 -XX:G1HeapRegionSize=1048576 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=134217728 -XX:MarkStackSize=4194304 -XX:MaxHeapSize=734003200 -XX:MaxNewSize=440401920 -XX:MinHeapDeltaBytes=1048576 -XX:MinHeapSize=8388608 -XX:NonNMethodCodeHeapSize=5839372 -XX:NonProfiledCodeHeapSize=122909434 -XX:ProfiledCodeHeapSize=122909434 -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:SoftMaxHeapSize=734003200 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseG1GC -XX:-UseNUMA -XX:-UseNUMAInterleaving
类似的,其他命令(VM.command_line, VM.uptime, VM.dynlibs) 也提供了其他一些非常有用的属性和基本信息,不做一一演示了。
Thread.print
之前介绍的指令都是介绍JVM的各种基本参数。现在来看一下跟线程相关的指令,这对JVM故障排除非常有帮助。Thread.print命令用于打印所有运行线程的堆栈信息,获取线程状态。
AndydeMacBook-Pro:bin andy$ jcmd 80180 Thread.print
80180:
2022-12-07 06:45:22
Full thread dump Java HotSpot(TM) 64-Bit Server VM (17.0.5+9-LTS-191 mixed mode, sharing):
Threads class SMR info:
_java_thread_list=0x00007fa2446ab370, length=13, elements={
0x00007fa244818000, 0x00007fa24481a800, 0x00007fa245092e00, 0x00007fa245812600,
0x00007fa24580ec00, 0x00007fa245811200, 0x00007fa245811800, 0x00007fa245811e00,
0x00007fa244819200, 0x00007fa24501d800, 0x00007fa245813400, 0x00007fa244803800,
0x00007fa2458d7e00
}
"Reference Handler" #2 daemon prio=10 os_prio=31 cpu=0.96ms elapsed=10406.56s tid=0x00007fa244818000 nid=0x4203 waiting on condition [0x00007000076ef000]
java.lang.Thread.State: RUNNABLE
at java.lang.ref.Reference.waitForReferencePendingList(java.base@17.0.5/Native Method)
at java.lang.ref.Reference.processPendingReferences(java.base@17.0.5/Reference.java:253)
at java.lang.ref.Reference$ReferenceHandler.run(java.base@17.0.5/Reference.java:215)
"Finalizer" #3 daemon prio=8 os_prio=31 cpu=0.51ms elapsed=10406.56s tid=0x00007fa24481a800 nid=0x4503 in Object.wait() [0x00007000077f2000]
java.lang.Thread.State: WAITING (on object monitor)
...
GC.class_histogram
该参数输出JVM 堆使用情况的相关信息。需要注意的是,她将列出所有实例的信息(包含外部类或应用程序类)
AndydeMacBook-Pro:bin andy$ jcmd 80180 GC.class_histogram
80180:
num #instances #bytes class name (module)
-------------------------------------------------------
1: 25312 4789808 [B (java.base@17.0.5)
2: 24623 590952 java.lang.String (java.base@17.0.5)
3: 1676 531360 [I (java.base@17.0.5)
4: 4206 508080 java.lang.Class (java.base@17.0.5)
5: 11604 371328 java.util.concurrent.ConcurrentHashMap$Node (java.base@17.0.5)
6: 3726 356904 [Ljava.lang.Object; (java.base@17.0.5)
7: 5806 185792 java.util.HashMap$Node (java.base@17.0.5)
8: 6847 109552 java.lang.Object (java.base@17.0.5)
...
GC.heap_dump
故障排查时,通常需要将堆栈信息导出为dump文件,以便开发人员做事后问题分析、定位。跟jmap dump -pid 一样 GC.heap_dump 命令可以导出dump文件
AndydeMacBook-Pro:Downloads andy$ jcmd 80180 GC.heap_dump ./demo_heap_dump
80180:
Dumping heap to ./demo_heap_dump ...
Heap dump file created [15855508 bytes in 0.246 secs]
JFR
Jcmd 使用JFR来分析应用程序性能。JFR(或Java Flight Recorder)是一个内置于JDK中的评测和事件收集框架。JFR允许我们收集有关JVM和Java应用程序行为的详细低级信息。
AndydeMacBook-Pro:Downloads andy$ jcmd 80180 JFR.start name=demo_recording settings=profile delay=10s duration=20s filename=./demorecording.jfr
80180:
Recording 1 scheduled to start in 10 s. The result will be written to:
/Users/andy/Library/Caches/JetBrains/IdeaIC2022.2/compile-server/demorecording.jfr
VM.native_memory
该选项可以提供关于JVM上的堆和非堆内存的许多有用细节信息。因此,对于可以用于调整内存使用情况并检测任何内存泄漏。JVM内存可粗略的分为堆内存和非堆内存。该命令可以获得完整JVM内存使用情况的详细信息。此外,这在定义基于容器的应用程序的内存大小时非常有用。
AndydeMacBook-Pro:Downloads andy$ jcmd 80180 VM.native_memory
80180:
Native Memory Tracking:
Total: reserved=1159598KB, committed=657786KB
- Java Heap (reserved=524288KB, committed=524288KB)
(mmap: reserved=524288KB, committed=524288KB)
- Class (reserved=279652KB, committed=29460KB)
(classes #6425)
内存泄露检测
特定情况下,需要识别JVM中是否存在内存泄露。首先需要定义一个时间基线,然后监控一段时间,以了解内存是否持续增加。因此内存泄露检测需要分两部
AndydeMacBook-Pro:Downloads andy$ jcmd 80180 VM.native_memory baseline
80180:
Baseline succeeded
AndydeMacBook-Pro:Downloads andy$ jcmd 80180 VM.native_memory summary.diff
80180:
Native Memory Tracking:
Total: reserved=1162150KB +2540KB, committed=660930KB +3068KB