前言

本次博客测试环境使用的是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 :生成堆转储快照;

在测试每个参数之前,我们先用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.7M
use(使用率):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 生成堆转储快照

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功能;这就是它不可替换的原因;

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐