JDK 工具之 jmap 详解
概述
jmap 命令用于输出与内存相关的统计信息,可以作用于运行中的虚拟机或者 core 文件。
JDK8 引入了 Java Mission Control、Java Flight Recorder 和 jcmd 等实用工具,用于诊断 JVM 和 Java 应用程序的问题。建议使用最新的工具 jcmd 来代替 jmap 以获得更强的的诊断功能和更低的性能开销。
注:以下命令行中的 <pid>
表示某个 jvm 进程的 pid。另外本文混用 java 进程和 jvm 进程。
输出 JVM 加载共享对象信息
如果 jmap 在没有任何命令行选项的情况下与进程或 core 文件一起使用,那么它将打印已加载的共享对象列表 可以使用 jps 获取指定 java 进程的 pid, 然后使用下面命令可以查看加载的共享库:
jmap <pid>
输出大概类似下面这样:
0x0000555d96e00000 8K /home/saltyfish/Public/jdk1.8.0_301/bin/java
...
0x00007f3257df3000 153K /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
0x00007f3257e18000 50K /usr/lib/x86_64-linux-gnu/libnss_files-2.31.so
0x00007f3257e2f000 186K /usr/lib/x86_64-linux-gnu/ld-2.31.so
此外,JDK7 引入了选项 -dump:format=b,file=filename,它使得 jmap 可以将 Java 堆以二进制 HPROF 格式转储到指定的文件中。然后可以使用 jhat 工具对该文件进行分析。
如果 jmap pid 命令由于进程挂起而无响应,可以使用 -F
选项(仅适用于 Oracle Solaris 和 Linux 操作系统)来强制使用 Serviceability Agent。
统计对象的个数和内存占用
这个就很有用了,可以用于诊断内存泄露,如果某些对象的个数明显不正常或者持续增加,那么很有可能便是内存泄露。
jmap -histo <pid>
输出样例如下:
num #instances #bytes class name
----------------------------------------------
1: 638 4272752 [I
2: 1464 1218728 [B
3: 5816 616208 [C
4: 4593 110232 java.lang.String
5: 672 76784 java.lang.Class
6: 1281 56824 [Ljava.lang.Object;
7: 623 24920 java.util.LinkedHashMap$Entry
8: 318 16320 [Ljava.lang.String;
9: 391 12512 java.util.HashMap$Node
...
num 是序号,instances 表示对象实例个数,bytes 表示占用的字节数,class name 表示是类名。从上面的输出可以看出内存排名前三的分别是 [I
、[B
和 [C
,分别表示整形数组、字节数组和字符数组。[
半边括号表示数组。
注意,运行该命令的时候应用程序线程会停止:
jmap -histo
会停止 Java 线程,而jmap -heap
会停止整个JVM 进程。暂停的持续时间可能会相当长,尤其是堆内存比较大的时候,遍历 4GB 的堆可能需要几秒钟的时间。
其实这还不算慢的,当使用 -F
得时候,工作机制就不同了,会使用 ptrace 读 jvm 内存,一次一个字节的读,这会相当的慢,慢到坑爹。具体可以参考:jmap -F / jstack -F。
导出 Heap dump 文件
可以借助 jmap 导出 heap dump 文件,之后可以使用更专业的工具,例如 jhat/MAT 来分析内存泄露问题。 下面的命令会导出 heap dump 并存储到 myapp.hprof:
jmap -dump:format=b,file=myapp.hprof <pid>
生产环境使用需要注意,该命令导致 stop the world,因此需要谨慎,不过如果部署了多台应用服务器,可以将有问题的服务器踢下线,然后使用 jmap 来获取 heap dump。
使用 jcmd 也可以完成同样的功能,命令如下:
jcmd <pid> GC.heap_dump </path/to/save>.hprof
jmap 默认 dump 所有的对象,jcmd 默认 dump live-objects。因此一般情况下 jcmd 更快。
统计 classloader
前面的是统计对堆内存,对于 Meta space,则可以使用使用 jmap -clsstats
命令查看各个加载器加载了多少个 class,以及 class 占用的 bytes。
jmap -clstats <pid>
输出样例如下,内容有删减:
class_loader classes bytes parent_loader alive? type
<bootstrap> 588 1101559 null live <internal>
0x000000076d6600e0 4 5106 0x000000076d63d7a8 live sun/misc/Launcher$AppClassLoader@0x00000007c000f958
...
温馨提示:反馈需要登录