介绍:
jmap是JDK,java 虚拟机 JVM 自带的工具软件,用于生成Java进程的内存映像文件(heap dump)。也就是说可以使用jmap生成Heap Dump;如果程序内存不足或者频繁GC,很有可能存在内存泄露情况,这时候就要借助Java堆Dump查看对象的情况。
工作原理总结:
- jmap 通过 JMX 与 Java 进程通信,获取其内存信息。
- 获取的内存信息包括堆的使用情况、对象统计等。
- 将获取的信息输出到文件中,生成标准的堆转储文件
什么是 java dump,如何获取:
dump
通常指的是从一个系统或应用程序中生成一份详细的信息快照,以便进行调试、分析或故障排除。在不同的上下文中,dump
可能指的是不同类型的信息。
线程 Dump:包含所有线程的运行状态,纯文本格式
堆 Dump:包含线程 Dump,堆对象的状态,二进制格式
- 使用 JVM 制作 Dump:指示虚拟机在发生内存不足错误时,自动生成堆Dump
ruby
-XX:+HeapDumpOnOutOfMemoryError
- 图形化工具制作:java VisualVM 3. 使用命令制作:jstack(打印线程的栈信息,制作线程 Dump),jmap(打印内存映射,制作堆 Dump)
注意 :对线程/堆进行 Dump 时(执行 jstack,jmap 等命令时候)是想要获取线程或者堆在特定时刻的状态和信息。为了确保这些信息的准确性和一致性,JVM在进行Dump时会暂停所有线程。也需要进入安全点才行。所以 dump 可能会导致进程卡住风险(生产谨慎操作),为了减少对生产环境的影响,可以选择合适的时机和条件来生成内存转储
使用:
jstack
常用来打印Java进程/core文件/远程调试端口的Java线程堆栈跟踪信息,包含当前虚拟机中所有线程正在执行的方法堆栈信息的集合。
主要用来定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待。
less
jstack [ options ] pid //Java进程
jstack [ options ] executable core //core文件
jstack [ options ] [ server-id@ ] remote-hostname-or-IP //远程调试端口
arduino
[root@cnetos test]# jstack
Usage:
jstack [-l] <pid>
(to connect to running process)
jstack -F [-m] [-l] <pid>
(to connect to a hung process)
jstack [-m] [-l] <executable> <core>
(to connect to a core file)
jstack [-m] [-l] [server_id@]<remote server IP or hostname>
(to connect to a remote debug server)
Options:
-F to force a thread dump. Use when jstack <pid> does not respond (process is hung)
-m to print both java and native frames (mixed mode)
-l long listing. Prints additional information about locks
-h or -help to print this help message
-----------------------------------------------------------------------------------------
-F:当正常输出的请求不被响应时,强制输出线程堆栈
-l:除了堆栈外,显示关于锁的附加信息
-m:如果调用到本地方法的话,可以显示C/C++的堆栈信息
gc:GC 相关的统计信息。
php
[root@cnetos test]# jstack 4611
2025-06-15 14:30:46
Full thread dump OpenJDK 64-Bit Server VM (25.412-b08 mixed mode):
"Attach Listener" #8 daemon prio=9 os_prio=0 tid=0x00007f840c001000 nid=0x1243 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Service Thread" #7 daemon prio=9 os_prio=0 tid=0x00007f8444128000 nid=0x120d runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007f844411d000 nid=0x120c waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007f844411a800 nid=0x120b waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007f844410c000 nid=0x120a runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007f84440e0000 nid=0x1209 in Object.wait() [0x00007f84275f4000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000dc988f00> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
- locked <0x00000000dc988f00> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:188)
"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007f84440db800 nid=0x1208 in Object.wait() [0x00007f84276f5000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000dc986b98> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x00000000dc986b98> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"main" #1 prio=5 os_prio=0 tid=0x00007f844404b800 nid=0x1204 waiting on condition [0x00007f844b1bc000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at Test.main(Test.java:11)
"VM Thread" os_prio=0 tid=0x00007f84440d1800 nid=0x1207 runnable
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007f844405e000 nid=0x1205 runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007f8444060000 nid=0x1206 runnable
"VM Periodic Task Thread" os_prio=0 tid=0x00007f844412e800 nid=0x120e waiting on condition
JNI global references: 5
----------------------------------------------------------------------------------------------------
Service Thread:线程名称
prio:该线程JVM中的优先级
os_prio:该线程在OS中的优先级
tid:JVM内的thread id (Java-level thread ID)
nid:Native thread ID,本地操作系统相关的线程id
daemon:表明这个线程是一个守护线程
[root@cnetos test]#
bash
"main" #1 prio=5 os_prio=0 tid=0x00007f844404b800 nid=0x1204 waiting on condition [0x00007f844b1bc000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at Test.main(Test.java:11)
对应到了程序 的 11 行
csharp
[root@cnetos test]# cat Test.java
public class Test {
public static void main(String[] args) throws InterruptedException {
System.out.println("Test project started at " + System.currentTimeMillis());
// 循环运行(每10秒输出一次,持续1小时)
int count = 0;
final long endTime = System.currentTimeMillis() + 3600 * 1000; // 1小时
while (System.currentTimeMillis() < endTime) {
System.out.println("Running... " + count++ + " times");
Thread.sleep(10000); // 休眠10秒
}
System.out.println("Test project completed normally");
}
}
线程堆栈信息如下:
php
"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007f84440db800 nid=0x1208 in Object.wait() [0x00007f84276f5000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000dc986b98> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x00000000dc986b98> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
我们能看到:
线程的状态: WAITING 线程的调用栈 线程的当前锁住的资源:<0x00000000dc986b98>
线程当前等待的资源:<0x00000000dc986b98>
为什么同时锁住的等待同一个资源:
线程的执行中,先获得了这个对象的 Monitor(对应于 locked<0x00000000dc986b98>)。当执行到obj.wait(),线程即放弃了 Monitor的所有权进入 "wait set"队列(对应于 waiting on<0x00000000dc986b98>)。
结合下图 线程状态图 理解:

线程状态说明:
NEW
:当线程对象创建时存在的状态,此时线程不可能执行;RUNNABLE
:当调用thread.start()后,线程变成为Runnable状态。只要得到CPU,就可以执行;RUNNING
:线程正在执行;WAITING
:执行thread.join()或在锁对象调用obj.wait()等情况就会进该状态,表明线程正处于等待某个资源或条件发生来唤醒自己;TIMED_WAITING
:执行Thread.sleep(long)、thread.join(long)或obj.wait(long)等就会进该状态,与Waiting的区别在于Timed_Waiting的等待有时间限制;BLOCKED
:如果进入同步方法或同步代码块,没有获取到锁,则会进入该状态;DEAD
:线程执行完毕,或者抛出了未捕获的异常之后,会进入dead状态,表示该线程结束。
jstack日志关键字说明:
deadlock
:死锁Waiting on condition
:等待某个资源或条件发生来唤醒自己。具体需要结合jstacktrace来分析,比如线程正在sleep,网络读写繁忙而等待Blocked
:阻塞waiting on monitor entry
:在等待获取锁in Object.wait()
:获取锁后又执行obj.wait()放弃锁
jstack
命令本身并没有直接支持查看 GC 相关信息的选项
jstat
是用于监控 HotSpot
虚拟机统计信息的工具,可以用来查看 GC
相关的统计信息。例如,可以使用以下命令查看 GC
的统计信息:
xml
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
jstat -gc <pid> <interval> <count>
- option - 选项,我们一般使用-gcutil 查看gc情况
- vmid - VM的进程号,即当前运行的java进程号
- pid - Java 进程的进程标识符
- interval - 间隔时间,单位为秒或者毫秒
- count - 打印次数,如果缺省则打印无数次
yaml
[root@cnetos test]# jstat -gc 4611
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
4608.0 4608.0 0.0 0.0 27648.0 2212.3 73728.0 0.0 4480.0 873.5 384.0 76.0 0 0.000 0 0.000 0.000
输出信息解释:
S0C
:第一个幸存区的大小S1C
:第二个幸存区的大小S0U
:第一个幸存区的使用大小S1U
:第二个幸存区的使用大小EC
:伊甸园区的大小EU
:伊甸园区的使用大小OC
:老年代大小OU
:老年代使用大小MC
:方法区大小MU
:方法区使用大小CCSC
:压缩类空间大小CCSU
:压缩类空间使用大小YGC
:年轻代垃圾回收次数YGCT
:年轻代垃圾回收消耗时间FGC
:老年代垃圾回收次数FGCT
:老年代垃圾回收消耗时间GCT
:垃圾回收消耗总时间
单位:KB
可参考:
此处为语雀内容卡片,点击链接查看:www.yuque.com/hollis666/n...
Jmap:
ini
# 格式:jmap -dump:format=b,file=文件名.hprof <PID>
jmap -dump:format=b,file=heapdump.hprof 8475
# 输出示例:
Dumping heap to /root/test/heapdump.hprof ...
Heap dump file created
format=b:指定二进制格式(必选)
file=heapdump.hprof:指定输出文件名(可自定义路径)
live选项(可选):live会只保存存活对象(GC 后仍存在的对象),文件通常更小
less
[root@cnetos test]# jps
1096 Bootstrap
4825 Test
6057 Jps
[root@cnetos test]# jmap -dump:format=b,file=heapdump.hprof 4825
Dumping heap to /root/test/heapdump.hprof ...
文件已存在
[root@cnetos test]# jmap -heap 4825
Attaching to process ID 4825, please wait...
Error attaching to process: sun.jvm.hotspot.runtime.VMVersionMismatchException: Supported versions are 25.121-b13. Target VM is 25.262-b10
sun.jvm.hotspot.debugger.DebuggerException: sun.jvm.hotspot.runtime.VMVersionMismatchException: Supported versions are 25.121-b13. Target VM is 25.262-b10
at sun.jvm.hotspot.HotSpotAgent.setupVM(HotSpotAgent.java:435)
at sun.jvm.hotspot.HotSpotAgent.go(HotSpotAgent.java:305)
at sun.jvm.hotspot.HotSpotAgent.attach(HotSpotAgent.java:140)
at sun.jvm.hotspot.tools.Tool.start(Tool.java:185)
at sun.jvm.hotspot.tools.Tool.execute(Tool.java:118)
at sun.jvm.hotspot.tools.HeapSummary.main(HeapSummary.java:49)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.tools.jmap.JMap.runTool(JMap.java:201)
at sun.tools.jmap.JMap.main(JMap.java:130)
Caused by: sun.jvm.hotspot.runtime.VMVersionMismatchException: Supported versions are 25.121-b13. Target VM is 25.262-b10
at sun.jvm.hotspot.runtime.VM.checkVMVersion(VM.java:227)
at sun.jvm.hotspot.runtime.VM.<init>(VM.java:294)
at sun.jvm.hotspot.runtime.VM.initialize(VM.java:370)
at sun.jvm.hotspot.HotSpotAgent.setupVM(HotSpotAgent.java:431)
... 11 more
问题一:由于 JDK 版本不匹配 导致的。jmap
工具与目标 Java 进程的 JVM 版本不一致,无法进行连接。
解决方法:改版本
bash
java -version # 应显示1.8.0_262
javac -version # 应显示1.8.0_262
jmap -version # 应显示8u262-b10
ini
[root@cnetos test]# jps
8614 Jps
1096 Bootstrap
8475 Test
[root@cnetos test]# jmap -heap $NEW_PID
Attaching to process ID 8475, please wait... //正在连接到进程ID为8475的进程,请稍等...
Debugger attached successfully. //成功附调试器加
Server compiler detected. //检测到服务器编译器
JVM version is 25.412-b08 //JVM版本号
using thread-local object allocation. //使用线程本地对象分配
Parallel GC with 2 thread(s) //GC方式
Heap Configuration: //堆内存初始化配置
MinHeapFreeRatio = 0 //最小的可用于自由空间的堆比例,对应jvm参数
MaxHeapFreeRatio = 100 //最大的可用于自由空间的堆比例,对应jvm参数
MaxHeapSize = 1782579200 (1700.0MB) //堆的最大大小,对应jvm参数
NewSize = 37748736 (36.0MB) //新生代初始大小,对应jvm参数
MaxNewSize = 594018304 (566.5MB) //新生代最大大小,对应jvm参数
OldSize = 75497472 (72.0MB) //老年代初始大小,对应jvm参数
NewRatio = 2 //新老年代比例,对应jvm参数
SurvivorRatio = 8 //Eden区和Suriver区的比例,对应jvm参数
MetaspaceSize = 21807104 (20.796875MB) //元空间的初始化大小,对应jvm参数
CompressedClassSpaceSize = 1073741824 (1024.0MB) // 压缩类空间的大小,对应jvm参数
MaxMetaspaceSize = 17592186044415 MB // 元空间最大大小,对应jvm参数
G1HeapRegionSize = 0 (0.0MB) // 使用G1收集器时的堆区域大小
Heap Usage: // 部分显示java堆的使用情况,包括各区域容量,已用空间和空闲空间:
PS Young Generation //表示Parallel Scavenge GC中的新生代(Parallel Scavenge是一种垃圾收集器)
Eden Space: //伊甸园区内存分布
capacity = 28311552 (27.0MB) //区总容量
used = 1132488 (1.0800247192382812MB) //已使用的容量
free = 27179064 (25.91997528076172MB) //剩余容量
4.000091552734375% used //Eden区的使用比率
From Space: //幸存者(Survivor)区中的From区域的容量、已用空间和空闲空间。
capacity = 4718592 (4.5MB)
used = 0 (0.0MB)
free = 4718592 (4.5MB)
0.0% used
To Space: //幸存者(Survivor)区中的To区域的容量、已用空间和空闲空间。
capacity = 4718592 (4.5MB)
used = 0 (0.0MB)
free = 4718592 (4.5MB)
0.0% used
PS Old Generation //表示Parallel Scavenge GC中的老年代。
capacity = 75497472 (72.0MB)
used = 0 (0.0MB)
free = 75497472 (72.0MB)
0.0% used
715 interned Strings occupying 47672 bytes. //表示共有715 个interned字符串占用了47672 字节的内存。
[root@cnetos test]#
jmap 参数总览:

查看堆内存中的对象数量及大小:
[root@cnetos test]# jmap -histo 8475
yaml
[root@cnetos test]# jmap -histo 8475
num #instances #bytes class name
----------------------------------------------
1: 419 1211960 [I
2: 2384 220552 [C
3: 470 54136 java.lang.Class
4: 195 52368 [B
5: 1493 35832 java.lang.String
6: 573 27504 java.nio.HeapCharBuffer
7: 524 26592 [Ljava.lang.Object;
8: 401 9624 java.lang.StringBuilder
9: 110 7920 java.lang.reflect.Field
10: 258 4128 java.lang.Integer
11: 95 3800 java.lang.ref.SoftReference
12: 116 3712 java.util.Hashtable$Entry
13: 73 2400 [Ljava.lang.String;
14: 6 2256 java.lang.Thread
15: 38 1824 sun.util.locale.LocaleObjectCache$CacheEntry
16: 56 1792 java.io.File
17: 12 1760 [Ljava.util.Hashtable$Entry;
18: 55 1760 java.util.concurrent.ConcurrentHashMap$Node
19: 21 1344 java.net.URL
20: 14 1120 [S
21: 2 1064 [Ljava.lang.invoke.MethodHandle;
22: 1 1040 [Ljava.lang.Integer;
23: 26 1040 java.io.ObjectStreamField
24: 11 944 [Ljava.util.HashMap$Node;
25: 27 864 java.util.HashMap$Node
26: 20 800 sun.util.locale.BaseLocale$Key
27: 17 680 java.util.LinkedHashMap$Entry
28: 12 672 java.lang.Class$ReflectionData
29: 8 640 java.lang.reflect.Constructor
30: 13 624 java.util.HashMap
31: 38 608 java.lang.Object
32: 19 608 java.util.Locale
33: 19 608 sun.util.locale.BaseLocale
34: 25 600 sun.security.action.GetPropertyAction
35: 8 528 [Ljava.lang.reflect.Field;
36: 5 528 [Ljava.util.concurrent.ConcurrentHashMap$Node;
37: 9 504 sun.misc.URLClassPath$JarLoader
38: 20 480 java.util.Locale$LocaleKey
......
num
:序号,表示第几行。#instances
:实例数量,表示内存中存在多少个该类的实例。#bytes
:内存大小,表示该类实例所占用的总字节数。class name
:类名,表示相应实例的类名。
统计 heap 中所有生存的对象的情况
统计 heap 中所有生存的对象的情况, 这个命令会先触发 gc 再统计:
arduino
# jmap -histo:live pid
jmap -histo:live 42626
使用 jhat 分析工具

erlang
[root@cnetos test]# jhat -port 5000 heapdump.hprof
Reading from heapdump.hprof...
Dump file created Sun Jun 15 00:26:47 CST 2025
Snapshot read, resolving...
Resolving 6127 objects...
Chasing references, expect 1 dots.
Eliminating duplicate references.
Snapshot resolved.
Started HTTP server on port 5000
Server is ready.
使用浏览器查看:

通过 jhat 的 Web 界面,你可以直观地查看堆内存中的对象分布和引用关系。如果发现特定类或对象存在异常,可以进一步结合代码分析内存使用逻辑
dump 日志收集与分析(jmap 和 jstack 工具)讲解与操作就先到这里了,欢迎指正,有任何疑问也可关注我公众号:打码大师
,进行技术交流,如本篇文章对您有所帮助,麻烦帮忙一键三连(点赞、转发、收藏)~
参考:
此处为语雀内容卡片,点击链接查看:www.yuque.com/hollis666/n...
此处为语雀内容卡片,点击链接查看:www.yuque.com/hollis666/n...
此处为掘金社区内容,点击链接查看: juejin.cn/post/730793...