6.2 jmap命令详解

4,471 阅读5分钟

jmap的作用:

监控内存内的Java对象

语法

  • jmap [option] (to connect to running process)
  • jmap [option] <executable (to connect to a core file)
  • jmap [option] [server_id@] (to connect to remote debug server)
选项说明

option:命令选项,常用选项如下:

  • -heap:打印Java堆概要信息,包括使用的GC算法、堆配置参数和各代中堆内存使用情况;
  • -histo[:live]: 打印Java堆中对象直方图,通过该图可以获取每个class的对象数目,占用内存大小和类全名信息,带上:live,则只统计活着的对象;
  • -permstat 打印永久代统计信息;
  • -finalizerinfo 打印等待回收的对象信息
  • -dump: 以hprof二进制格式将Java堆信息输出到文件内,该文件可以用MAT、VisualVM或jhat等工具查看;

dump-options选项:

  • live 只输出活着的对象;不指定,则输出堆中所有对象
  • format=b 指定输出格式为二进制
  • file= 指定文件名及文件存储位置,例如:jmap -dump:live,format=b,file=D:\heap.bin
  • -F 与-dump: 或-histo一起使用,当没有响应时,强制执行;注意:不支持live子选项
  • pid:进程id

jmap -heap pid

> jmap -heap 10352

jmap -heap 10352
Attaching to process ID 10352, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.201-b09

using thread-local object allocation.
Parallel GC with 4 thread(s)

Heap Configuration:
   //对应jvm启动参数-XX:MinHeapFreeRatio设置JVM堆最小空闲比率(defalut 40)
   MinHeapFreeRatio         = 0 
   //对应jvm启动参数 -XX:MaxHeapFreeRatio设置JVM堆最大空闲比率(default 70)
   MaxHeapFreeRatio         = 100
   //对应jvm启动参数-XX:MaxHeapSize=设置JVM堆的最大大小
   MaxHeapSize              = 4280287232 (4082.0MB)
   //对应jvm启动参数-XX:NewSize=设置JVM堆的‘新生代’的默认大小
   NewSize                  = 89128960 (85.0MB)
   //对应jvm启动参数-XX:MaxNewSize=设置JVM堆的‘新生代’的最大大小
   MaxNewSize               = 1426587648 (1360.5MB)
   //对应jvm启动参数-XX:OldSize=<value>:设置JVM堆的‘老年代’的大小
   OldSize                  = 179306496 (171.0MB)
   //对应jvm启动参数-XX:NewRatio=:‘新生代’和‘老生代’的大小比率
   NewRatio                 = 2
   //对应jvm启动参数-XX:SurvivorRatio=设置年轻代中Eden区与Survivor区的大小比值
   SurvivorRatio            = 8
   //对应jvm启动参数-XX:MetaspaceSize=<value>:设置JVM堆的‘元空间’的初始大小
   // jdk1.8 永久代已经被元空间所取代
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   //对应jvm启动参数-XX:MaxMetaspaceSize= :设置JVM堆的‘元空间’的最大大小
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)
//堆内存分布
Heap Usage:
//新生代的内存分布
PS Young Generation
//Eden区内存分布
Eden Space:
   //Eden区总容量
   capacity = 1425539072 (1359.5MB)
   //Eden区已使用
   used     = 28510792 (27.19001007080078MB)
   //Eden区剩余容量
   free     = 1397028280 (1332.3099899291992MB)
   //Eden区使用比率
   2.0000007407724003% used
From Space:
   capacity = 524288 (0.5MB)
   used     = 65536 (0.0625MB)
   free     = 458752 (0.4375MB)
   12.5% used
To Space:
   capacity = 524288 (0.5MB)
   used     = 0 (0.0MB)
   free     = 524288 (0.5MB)
   0.0% used
PS Old Generation
   capacity = 128974848 (123.0MB)
   used     = 24006808 (22.894676208496094MB)
   free     = 104968040 (100.1053237915039MB)
   18.613557893086256% used

13410 interned Strings occupying 1194568 bytes.
展示class的内存情况

jmap -histo pid

>jmap -histo 10352

 num     #instances         #bytes  class name
----------------------------------------------
   1:      27073797      433180752  java.lang.Integer
   2:          5442       16503144  [I
   3:          5005        6143944  [B
   4:         33835        3446608  [C
   5:          9721         855448  java.lang.reflect.Method
   6:         33751         810024  java.lang.String
   7:          6494         717416  java.lang.Class
   8:         14303         457696  java.util.concurrent.ConcurrentHashMap$Node
   ...

classname 中的非自定义类含义:

BaseType Character Type Interpretation
B byte signed byte
C char Unicode character
D double double-precision floating-point value
F float single-precision floating-point value
I int integer
J long long integer
L reference an instance of class
S short signed short
Z boolean true or false
[ reference one array dimension
heap中所有生存的对象的情况, 这个命令会先触发gc,再统计

jmap -histo:live pid

> jmap -histo:live 10352

 num     #instances         #bytes  class name
----------------------------------------------
   1:         27483        2670608  [C
   2:          6453         713152  java.lang.Class
   3:         27300         655200  java.lang.String
   4:         12861         411552  java.util.concurrent.ConcurrentHashMap$Node
   5:          6364         327688  [Ljava.lang.Object;
   6:          2496         219648  java.lang.reflect.Method
   7:           874         178440  [B
   ...
将内存使用的详细情况输出到文件

jmap -dump:live,format=b,file=a.log pid 执行这个命令,JVM会将整个heap的信息dump写入到一个文件,heap如果比较大的话,就会导致这个过程比较耗时,并且执行的过程中为了保证dump的信息是可靠的,所以会暂停应用

该命令通常用来分析内存泄漏OOM,通常做法是:

  • 首先配置JVM启动参数,让JVM在遇到OutOfMemoryError时自动生成Dump文件
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path
  • 然后使用命令
jmap  -dump:format=b,file=/path/heap.bin 进程ID  

如果只dump heap中的存活对象,则加上选项-live。

  • 然后使用MAT分析工具,如jhat命令,eclipse的mat插件

实例

1. 通过命令查看大对象

jmap -histo <pid>|less

2. 查看对象数最多的对象,并按降序排序输出

jmap -histo <pid>|grep alibaba|sort -k 2 -g -r|less

3. 查看占用内存最多的最象,并按降序排序输出:

jmap -histo <pid>|grep alibaba|sort -k 3 -g -r|less

总结

  • 如果程序内存不足或者频繁GC,很有可能存在内存泄露情况,这时候就要借助Java堆Dump查看对象的情况。
  • 要制作堆Dump可以直接使用jvm自带的jmap命令
  • 可以先使用jmap -heap命令查看堆的使用情况,看一下各个堆空间的占用情况。
  • 使用jmap -histo:[live]查看堆内存中的对象的情况。如果有大量对象在持续被引用,并没有被释放掉,那就产生了内存泄露,就要结合代码,把不用的对象释放掉。
  • 也可以使用 jmap -dump:format=b,file=命令将堆信息保存到一个文件中,再借助jhat命令查看详细内容
  • 在内存出现泄露、溢出或者其它前提条件下,建议多dump几次内存,把内存文件进行编号归档,便于后续内存整理分析。
  • 在用cms gc的情况下,执行jmap -heap有些时候会导致进程变T,因此强烈建议别执行这个命令,如果想获取内存目前每个区域的使用状况,可通过jstat -gc或jstat -gccapacity来拿到