前言
本次博客测试环境使用的是JDK1.8
,系统使用CentOS Linux release 7.4.1708 (Core)
什么是jmap
jmap是一个多功能的命令,也是jdk自带的命令,是我们在开发过程中用来虚拟机性能能分析和故障解决必不可少的工具之一,它可以生产java程序的堆栈(dump
)文件;也可以查看堆内的对象信息、Classloader
类加载信息以及finalizer
队列(java将要回收的垃圾队列);另外,jmap没有图形界面,所有的操作都是在命令行完成的。
使用方法
命令格式:
jmap [options] <pid>
options参数命令详解
- no option:不带参数查询进程,就像这样
jmap 12271
,查看内存中共享对象信息;,类似Solaris pmap命令; - heap: 显示java堆详细信息
- histo[:live] :显示堆中对象的统计信息,加
:live
值打印存活的对象,如果不加:live
则查询所有的对象; - clstats:打印类加载信息
- finalizerinfo:显示在F-Queue队列等待被清理的对象;在发生GC之前某些对象可能要被回收,那么在回收之前,这些对象就会放到F-Queue队列中;清理时会执行对象的
finalizer()
方法; - dump<dump-options> :生成堆转储快照;
在测试每个参数之前,我们先用jps命令看看linux系统中有哪些java进程
太好了,有一个可以用的,这是我自己线上的项目,进程为12771
,等下就拿这个进程来测试;
no option 无参数查进程—查看内存中共享对象信息
jmap 12771
通过结果可以看到:
- 第一列打印的16进制是每个共享对象的其实地址
- 第二列是对象的映射大小
- 第三列是共享对象文件的路径全称
只是对于我们开发人员来说,这些信息对我们没有多大作用,所以了解一下即可,没必要深究。
1、 -heap 显示java堆详细信息
jmap -heap 12771
打印结果如下
[root@VM_0_5_centos ~]# jmap -heap 12771
Attaching to process ID 12771, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.232-b09
using thread-local object allocation.
Mark Sweep Compact GC
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 482344960 (460.0MB)
NewSize = 10485760 (10.0MB)
MaxNewSize = 160759808 (153.3125MB)
OldSize = 20971520 (20.0MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)
Heap Usage:
New Generation (Eden + 1 Survivor Space):
capacity = 22413312 (21.375MB)
used = 14306760 (13.643989562988281MB)
free = 8106552 (7.731010437011719MB)
63.83153011924342% used
Eden Space:
capacity = 19988480 (19.0625MB)
used = 12015896 (11.459251403808594MB)
free = 7972584 (7.603248596191406MB)
60.11410572489754% used
From Space:
capacity = 2424832 (2.3125MB)
used = 2290864 (2.1847381591796875MB)
free = 133968 (0.1277618408203125MB)
94.47516364020271% used
To Space:
capacity = 2424832 (2.3125MB)
used = 0 (0.0MB)
free = 2424832 (2.3125MB)
0.0% used
tenured generation:
capacity = 49614848 (47.31640625MB)
used = 33554184 (31.99976348876953MB)
free = 16060664 (15.316642761230469MB)
67.62931935214233% used
25942 interned Strings occupying 2999576 bytes.
heap 结果说明
MinHeapFreeRatio
空闲堆空间的最小百分比,计算公式为:HeapFreeRatio =(CurrentFreeHeapSize/CurrentTotalHeapSize) * 100,值的区间为0到100,默认值为 40。如果HeapFreeRatio < MinHeapFreeRatio,则需要进行堆扩容,扩容的时机应该在每次垃圾回收之后。MaxHeapFreeRatio
空闲堆空间的最大百分比,计算公式为:HeapFreeRatio =(CurrentFreeHeapSize/CurrentTotalHeapSize) * 100,值的区间为0到100,默认值为 70。如果HeapFreeRatio > MaxHeapFreeRatio,则需要进行堆缩容,缩容的时机应该在每次垃圾回收之后。MaxHeapSize
JVM 堆空间允许的最大值。NewSize
JVM 新生代堆空间的默认值。MaxNewSize
JVM 新生代堆空间允许的最大值。OldSize
JVM 老年代堆空间的默认值。NewRatio
新生代(2个Survivor区和Eden区 )与老年代(不包括永久区)的堆空间比值,表示新生代:老年代=1:2。SurvivorRatio
两个Survivor区和Eden区的堆空间比值为 8,表示 S0 : S1 :Eden = 1:1:8。MetaspaceSize
JVM 元空间的默认值。CompressedClassSpaceSize
Compressed Class Space 空间大小限制MaxMetaspaceSize
JVM 元空间允许的最大值。G1HeapRegionSize
在使用 G1 垃圾回收算法时,JVM 会将 Heap 空间分隔为若干个 Region,该参数用来指定每个 Region 空间的大小。
Heap Usage:
New Generation (Eden + 1 Survivor Space):
capacity = 22413312 (21.375MB)
used = 14306760 (13.643989562988281MB)
free = 8106552 (7.731010437011719MB)
63.83153011924342% used
新生代内存使用情况(伊甸区 + 1个Survivor区(S0区或S1区))
capacity
(容量):一共有21.375M
used
:已使用13.6M
free
:剩余7.7Muse(使用率)
:63.83%
Eden Space:
:伊甸区使用情况From Space:
:from区 (Survivor区)使用情况To Space:
:to区(Survivor区)使用情况tenured generation:
:老年代使用情况
2、-histo[:live] 显示堆中对象的统计信息(到目前为止还存活的对象)
jmap -histo:live 12771
打印结果如下,默认会以占用的大小倒序排列;
[root@VM_0_5_centos ~]# jmap -histo 12771
num #instances #bytes class name
----------------------------------------------
1: 86764 11326648 [C
2: 3817 4359520 [B
3: 6008 2580208 [I
4: 24207 2130216 java.lang.reflect.Method
5: 79367 1904808 java.lang.String
6: 12306 1374208 java.lang.Class
7: 39215 1254880 java.util.concurrent.ConcurrentHashMap$Node
8: 12569 777336 [Ljava.lang.Object;
9: 17820 570240 java.util.HashMap$Node
10: 14160 566400 java.util.LinkedHashMap$Entry
11: 6512 550864 [Ljava.util.HashMap$Node;
12: 27815 445040 java.lang.Object
13: 20176 444472 [Ljava.lang.Class;
14: 7789 436184 java.util.LinkedHashMap
15: 5890 424080 java.lang.reflect.Field
16: 197 391320 [Ljava.util.concurrent.ConcurrentHashMap$Node;
如果我们想要打印占用空前最多的前5个对象,可以这么写:
[root@VM_0_5_centos ~]# jmap -histo:live 12771 | head -8
num #instances #bytes class name
----------------------------------------------
1: 95096 12216648 [C
2: 93199 2236776 java.lang.String
3: 5575 1618464 [B
4: 13052 1459872 java.lang.Class
5: 5824 1457256 [I
其中,head -8
表示打印结果的从头8行的内容,因为表头和横线还需要占一些空间;所以最终显示的就是有5行内容
结果说明
num
: 第一列为行号instances
: 第二列为对象的实例数量bytes
: 第三列为对象占用总大小,单位:字节class name
: 第四列为类的全局限定名,这里需要注意,[C
表示char[]
数组,[B
表示byte[]
数组,[I
表示int[]
数组
3、-clstats 打印类加载信息
jmap -clstats 12771
输入这个命令后需要等一会,我的linux配置比较低,等了一分钟才出来结果;所以这边使用windows系统来执行命令,结果如下
4、-finalizerinfo 显示在F-Queue队列等待被清理的对象
jmap -finalizerinfo 12771
打印结果如下,可以看到最后一行显示为0,表示当前队列中没有被清理的对象;这是因为垃圾清理过程的速度非常快,是毫秒级别的,所以大部分时间,这个队列里都是没有对象的,
[root@VM_0_5_centos ~]# jmap -finalizerinfo 12771
Attaching to process ID 12771, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.232-b09
Number of objects pending for finalization: 0
5、-dump<dump-options> 生成堆转储快照
jmap -dump:live,format=b,file=jmap.bin 12771
在执行之前,先说明下这个命令的参数
:live
: 只打印存活的对象format=b
:以二进制的格式进行存储file=jmap.bin
:将文件保存到当前目录,文件名称为jmap.bin
打印结果如下
[root@VM_0_5_centos ~]# jmap -dump:live,format=b,file=jmap.bin 12771
Dumping heap to /root/jmap.bin ...
Heap dump file created
这时候堆转储快照就已经生成了,二进制文件已经保存到目录:heap to /root/jmap.bin
了。
因为是二进制文件,所以这里面的信息直接看是看不到的
这个堆转储文件需要用jhat
命令或者jvisualvm
打开,jvisualvm
是图形界面,观看更加直观些,jhat
这个工具因为使用起来会启动内置的Http的服务器,生成的结果可在浏览器中观看,但是jhat这个工具现在已经很少使用了,主要原因有2个
- 1是这个功能比较简陋,因为有了其他更强大的工具来分析,比如
jvisualvm
- 2是一般不会在部署了应用程序的服务器上直接分析dump文件,就算要分析,也会拷贝到其他机器上进行分析,因为分析工作既耗时又耗硬件资源;除非逼不得已,不然不会这么做;
使用jhat
打开,命令如下,启动后就会启动内置的服务器,默认端口号:7000
;启动后直接在浏览器访问:localhost:7000
即可
jhat jmap.bin
完
其实jmap是一个不可替代的工具,至少当现在为止还没有出现替代的工具,在线上项目调优时,使用jmap -histo:live pid
命令会比较方便查看内存泄漏的原因,阿里巴巴的arthas
工具目前也还没有jmap的histo功能;这就是它不可替换的原因;