dump 日志收集与分析(jmap 和 jstack 工具)

介绍:

jmap是JDK,java 虚拟机 JVM 自带的工具软件,用于生成Java进程的内存映像文件(heap dump)。也就是说可以使用jmap生成Heap Dump;如果程序内存不足或者频繁GC,很有可能存在内存泄露情况,这时候就要借助Java堆Dump查看对象的情况。


工作原理总结:

  • jmap 通过 JMX 与 Java 进程通信,获取其内存信息。
  • 获取的内存信息包括堆的使用情况、对象统计等。
  • 将获取的信息输出到文件中,生成标准的堆转储文件

什么是 java dump,如何获取:

dump 通常指的是从一个系统或应用程序中生成一份详细的信息快照,以便进行调试、分析或故障排除。在不同的上下文中,dump 可能指的是不同类型的信息。

线程 Dump:包含所有线程的运行状态,纯文本格式

堆 Dump:包含线程 Dump,堆对象的状态,二进制格式

  1. 使用 JVM 制作 Dump:指示虚拟机在发生内存不足错误时,自动生成堆Dump
ruby 复制代码
-XX:+HeapDumpOnOutOfMemoryError
  1. 图形化工具制作: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...

相关推荐
我不是混子12 分钟前
什么是Java 的 Lambda 表达式?
java·后端
小蝙蝠侠23 分钟前
JMeter 执行流程
java·jmeter
程序员小假1 小时前
我们来说一说 ThreadLocal 内存泄漏
java·后端
xq95271 小时前
获取Facebook 散列利器 来了 十六进制到 Base64 转换器
java
我不是混子1 小时前
聊聊Spring事件机制
java·后端
DKPT1 小时前
JVM栈溢出时如何dump栈信息?
java·jvm·笔记·学习·spring
DKPT2 小时前
JVM堆大小如何设置?
java·开发语言·jvm·笔记·学习
铅笔侠_小龙虾2 小时前
JVM 目录
java·jvm
yunxi_052 小时前
让大模型会“说话”:基于 Spring WebSocket 的毫秒级流式 RAG 对话
java·后端
用户6120414922132 小时前
jsp+servlet做的医院挂号看诊管理系统
java·javascript·mysql