介绍:
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
2. 图形化工具制作: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.