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...

相关推荐
yuren_xia1 小时前
RabbitMQ 知识详解(Java版)
java·rabbitmq·java-rabbitmq
kfyty7252 小时前
轻量级 ioc 框架 loveqq,支持接口上传 jar 格式的 starter 启动器并支持热加载其中的 bean
java·jvm·ioc·jar·热加载
早起鸟儿3 小时前
docker-Dockerfile 配置
java·linux·运维·docker
云边小网安3 小时前
java集合篇(六) ---- ListIterator 接口
java·开发语言·青少年编程·java集合
都叫我大帅哥3 小时前
Spring WebFlux:响应式编程的“未来战士”还是“花架子”?
java·spring·flux
都叫我大帅哥3 小时前
Reactor 深度解析:响应式编程的「核反应堆」是如何工作的?
java·spring
不太厉害的程序员3 小时前
NC65配置xml找不到Bean
xml·java·后端·eclipse
我在北国不背锅3 小时前
基于Java开发的浏览器自动化Playwright-MCP服务器
java·playwright·mcp
LUCIAZZZ4 小时前
钉钉机器人-自定义卡片推送快速入门
java·jvm·spring boot·机器人·钉钉·springboot
优秀1354 小时前
java33
java