JVM调优常用命令
1.jps
专门用于查找 Java 进程。
1.1 基本语法
jps [options] [hostid]
[options]:命令选项,用于控制输出内容。
[hostid]:指定远程主机,可以省略(主要用于查看本机进程)。
1.2 常用选项(options)
选项 | 全称 | 作用描述 |
---|---|---|
-q | quiet | 只输出进程的 PID,不显示主类名称 |
-m | main | 输出传递给 main() 方法的参数 |
-l | long | 输出应用程序主类的完整包名或应用程序 JAR 文件的完整路径名 |
-v | version | 输出传递给 JVM 的参数(非常有用,常用于排查启动参数问题) |
-V | 输出通过 .hotspotrc 文件或 -XX:Flags= 指定的参数 |
1.3 使用示例
1.3.1基本使用
jps
1.3.2 显示完整名称 (-l
)
jps -l
1.3.3 显示 JVM 参数 (-v
)
jps -v
1.4.4 显示 main 方法的参数 (-m
)
jps -m
1.4.5 组合使用选项
常用的组合是 -mlv
,可以一次性看到所有信息。
jps -mlv
1.4.6 只显示 PID (-q
)
jps -q
2. jmap
此命令可以用来查看内存信息,实例个数以及占用内存大小。
2.1 基本语法
jmap [option] <pid> # 最常用的形式,对指定PID的Java进程操作
jmap [option] <executable <core> # 与核心文件交互
jmap [option] [server_id@]<remote server IP or hostname> # 与远程服务器交互
2.2 常用选项(options)
选项 | 作用描述 | 备注 |
---|---|---|
-dump:[live,]format=b,file=<filename> | 生成堆转储快照(核心功能) | live :只转储存活的对象,会触发 Full GC。format=b :指定二进制格式。file :指定输出文件名。 |
-heap | 显示Java堆的详细信息 | 包括GC算法、堆配置、各代内存使用情况。 |
-histo[:live] | 显示堆中对象的统计信息 | live :只统计存活的对象,会触发 Full GC。显示每个类的实例数、内存占用、类全名。 |
-clstats | 显示类加载器的统计信息 | 输出类加载器及其加载的类的元数据大小。 |
-finalizerinfo | 显示正在等待 Finalizer 线程执行 finalize 方法的对象 | 如果队列不为空,说明有对象在等待被回收。 |
-F | 强制模式 | 当 jmap -dump 或 jmap -histo 没有响应时,与 -dump 或 -histo 一起使用。(强制操作有风险,可能导致进程僵死) |
2.3 使用示例
2.3.1 生成堆转储文件
#触发Full GC
jmap -dump:live,format=b,file=myheapdump.hprof 3947#不触发Full GC
jmap -dump:format=b,file=myheapdump.hprof 3947
会在当前目录下生成一个名为 myheapdump.hprof
的二进制文件。
使用相关的分析工具打开(比如 JVisualVM ),可以直观地分析内存泄漏、大对象等问题。
可以设置内存溢出自动导出dump文件(内存很大的时候,可能会导不出来)
1. -XX:+HeapDumpOnOutOfMemoryError
2. -XX:HeapDumpPath=./ (路径)
注意事项:
使用
live
选项会触发一次 Full GC,可能会对线上应用的性能造成短暂影响,请谨慎在生产环境使用。堆转储文件通常非常大(可达数GB),确保磁盘空间充足。
2.3.2 查看堆内存摘要信息 (-heap
)
(mac权限问题使用命令jcmd 3947 GC.heap_info输出的,效果类似呈现可能有所不同)
2.3.3 查看类实例统计信息 (-histo
)
# 查看所有对象(包括已死但未回收的)
jmap -histo 3947 | head -20# 只查看存活的对象(会触发Full GC)
jmap -histo:live 3947 | head -20#输出到指定目录
jmap -histo 3947 > ./log.txt
|
是管道符,将jmap -histo 3947
的输出传递给head -20
。head
是一个命令行工具,用于显示文件或输入的前几行。-20
表示仅显示前 20 行。(方便查看)
- num:序号
- instances:实例数量
- bytes:占用空间大小
- class name:类名称
3. jstack
排查 CPU 占用过高问题:找出哪个线程、哪行代码在疯狂消耗 CPU。
诊断死锁(Deadlock):自动检测并报告线程间是否存在死锁。
分析线程阻塞(Blocking)或挂起(Hanging):查看线程为何无法继续执行,比如在等待锁、等待IO、等待网络响应等。
排查应用无响应:当应用不处理请求时,查看所有线程的状态和调用栈。
监控线程状态:查看线程总数、守护线程数、线程状态(RUNNABLE, BLOCKED, WAITING, TIMED_WAITING)等。
3.1 基本语法
jstack [options] <pid> # 最常用的形式,对指定PID的Java进程操作
jstack [options] <executable <core> # 从核心文件获取线程转储
jstack [options] [server_id@]<remote server IP or hostname> # 连接远程服务器
3.2 常用选项(options)
选项 | 作用描述 | 备注 |
---|---|---|
-F | 强制生成线程转储 | 当普通的 jstack 命令没有响应时使用(例如进程卡死时)。 |
-l | 长列表模式 | (最常用) 除了堆栈信息外,还显示关于锁的附加信息(如拥有的锁、等待的锁)。强烈建议始终使用此选项。 |
-m | 混合模式 | 输出 Java 和本地本地(Native C/C++)框架的堆栈信息。用于排查 JNI 代码问题。 |
-h | 显示帮助信息 |
3.3 使用示例
jstack -l 3947
2025-09-14 22:03:11
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.333-b02 mixed mode):"nacos-grpc-client-executor-192.168.3.2-1970" #2057 daemon prio=5 os_prio=31 tid=0x00007f7fbb134000 nid=0x1c07b waiting on condition [0x000000030fa60000]java.lang.Thread.State: TIMED_WAITING (parking)at sun.misc.Unsafe.park(Native Method)- parking to wait for <0x000000079682cdb0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)at java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:467)at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1073)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:750)Locked ownable synchronizers:- None
头部信息:时间、JVM 信息
2025-09-14 22:03:11
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.333-b02 mixed mode):
线程信息列表
"nacos-grpc-client-executor-192.168.3.2-1961" #2048 daemon prio=5 os_prio=31 tid=0x00007f7fbb943800 nid=0xb85f waiting on condition [0x000000030fa60000]java.lang.Thread.State: TIMED_WAITING (parking)at sun.misc.Unsafe.park(Native Method)- parking to wait for <0x000000079682cdb0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)at java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:467)at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1073)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:750)Locked ownable synchronizers:- None
"http-nio-8080-exec-1" #32 daemon prio=5 os_prio=31 tid=0x00007f9d8a810000 nid=0x5a03 runnable [0x0000700008bb9000]java.lang.Thread.State: RUNNABLEat java.net.SocketInputStream.socketRead0(Native Method)at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)at java.net.SocketInputStream.read(SocketInputStream.java:171)at java.net.SocketInputStream.read(SocketInputStream.java:141)at org.apache.coyote.http11.Http11InputBuffer.fill(Http11InputBuffer.java:794)at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:358)at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:447)at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893)at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1722)at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)- locked <0x0000000740008f58> (a org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)at java.lang.Thread.run(Thread.java:748)Locked ownable synchronizers:- None
关键字段解析:
线程名:
"nacos-grpc-client-executor-192.168.3.2-1961"
(通常能看出线程用途)守护线程:
daemon
(如果是,则会标明)优先级:
prio=5
线程状态:
java.lang.Thread.State: TIMED_WAITING (parking)
nid:
nid=0xb85f
(Native Thread ID,对应操作系统的线程ID,可用于和top -Hp <pid>
命令结合查找CPU消耗最高的线程)调用栈(Stack Trace):显示了该线程当前执行到哪个类的哪个方法。
锁信息:
- locked <...>
(如果有会显示)正在等待的锁:
- waiting on <...>
(如果有会显示)
shi
top
:找到 CPU 占用最高的 Java 进程 PID。top -Hp <pid>
:查看该进程内所有线程的 CPU 占用情况,找到占用最高的线程 ID(例如12345
)。将线程 ID 转换为十六进制:
printf "%x\n" 12345
-> 得到0x3039
。jstack -l <pid> > thread_dump.log
:生成线程转储。在
thread_dump.log
中搜索上一步得到的十六进制值nid=0x3039
。找到对应的线程,查看它的调用栈,就能定位到是哪一行代码在疯狂消耗 CPU。
3.4 死锁检测(如果有的话)
如果 jstack
检测到死锁,它会在转储的最后清晰地报告:
Found one Java-level deadlock:
=============================
"Thread-1":waiting to lock monitor 0x00007faa8b008f00 (object 0x00000007400c6d98, a java.lang.Object),which is held by "Thread-0"
"Thread-0":waiting to lock monitor 0x00007faa8b00a100 (object 0x00000007400c6da8, a java.lang.Object),which is held by "Thread-1"Java stack information for the threads listed above:
===================================================
"Thread-1":at com.example.DeadlockDemo.methodB(DeadlockDemo.java:30)- waiting to lock <0x00000007400c6d98> (a java.lang.Object)- locked <0x00000007400c6da8> (a java.lang.Object)at com.example.DeadlockDemo.run(DeadLockDemo.java:40)
"Thread-0":at com.example.DeadlockDemo.methodA(DeadlockDemo.java:16)- waiting to lock <0x00000007400c6da8> (a java.lang.Object)- locked <0x00000007400c6d98> (a java.lang.Object)at com.example.DeadlockDemo.run(DeadLockDemo.java:35)