原来jdk自带了这么好玩的工具 > jmap 使用教程

发布时间:2022-03-01 10:57:28 作者:yexindonglai@163.com 阅读(897)

前言

本次博客测试环境使用的是JDK1.8,系统使用CentOS Linux release 7.4.1708 (Core)

什么是jmap

jmap是一个多功能的命令,也是jdk自带的命令,是我们在开发过程中用来虚拟机性能能分析和故障解决必不可少的工具之一,它可以生产java程序的堆栈(dump)文件;也可以查看堆内的对象信息、Classloader 类加载信息以及finalizer队列(java将要回收的垃圾队列);另外,jmap没有图形界面,所有的操作都是在命令行完成的。

使用方法

命令格式:

  1. 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 无参数查进程—查看内存中共享对象信息

  1. jmap 12771

通过结果可以看到:

  • 第一列打印的16进制是每个共享对象的其实地址
  • 第二列是对象的映射大小
  • 第三列是共享对象文件的路径全称

只是对于我们开发人员来说,这些信息对我们没有多大作用,所以了解一下即可,没必要深究。
在这里插入图片描述

1、 -heap 显示java堆详细信息

  1. jmap -heap 12771

打印结果如下

  1. [root@VM_0_5_centos ~]# jmap -heap 12771
  2. Attaching to process ID 12771, please wait...
  3. Debugger attached successfully.
  4. Server compiler detected.
  5. JVM version is 25.232-b09
  6. using thread-local object allocation.
  7. Mark Sweep Compact GC
  8. Heap Configuration:
  9. MinHeapFreeRatio = 40
  10. MaxHeapFreeRatio = 70
  11. MaxHeapSize = 482344960 (460.0MB)
  12. NewSize = 10485760 (10.0MB)
  13. MaxNewSize = 160759808 (153.3125MB)
  14. OldSize = 20971520 (20.0MB)
  15. NewRatio = 2
  16. SurvivorRatio = 8
  17. MetaspaceSize = 21807104 (20.796875MB)
  18. CompressedClassSpaceSize = 1073741824 (1024.0MB)
  19. MaxMetaspaceSize = 17592186044415 MB
  20. G1HeapRegionSize = 0 (0.0MB)
  21. Heap Usage:
  22. New Generation (Eden + 1 Survivor Space):
  23. capacity = 22413312 (21.375MB)
  24. used = 14306760 (13.643989562988281MB)
  25. free = 8106552 (7.731010437011719MB)
  26. 63.83153011924342% used
  27. Eden Space:
  28. capacity = 19988480 (19.0625MB)
  29. used = 12015896 (11.459251403808594MB)
  30. free = 7972584 (7.603248596191406MB)
  31. 60.11410572489754% used
  32. From Space:
  33. capacity = 2424832 (2.3125MB)
  34. used = 2290864 (2.1847381591796875MB)
  35. free = 133968 (0.1277618408203125MB)
  36. 94.47516364020271% used
  37. To Space:
  38. capacity = 2424832 (2.3125MB)
  39. used = 0 (0.0MB)
  40. free = 2424832 (2.3125MB)
  41. 0.0% used
  42. tenured generation:
  43. capacity = 49614848 (47.31640625MB)
  44. used = 33554184 (31.99976348876953MB)
  45. free = 16060664 (15.316642761230469MB)
  46. 67.62931935214233% used
  47. 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:
  1. New Generation (Eden + 1 Survivor Space):
  2. capacity = 22413312 (21.375MB)
  3. used = 14306760 (13.643989562988281MB)
  4. free = 8106552 (7.731010437011719MB)
  5. 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] 显示堆中对象的统计信息(到目前为止还存活的对象)

  1. jmap -histo:live 12771

打印结果如下,默认会以占用的大小倒序排列;

  1. [root@VM_0_5_centos ~]# jmap -histo 12771
  2. num #instances #bytes class name
  3. ----------------------------------------------
  4. 1: 86764 11326648 [C
  5. 2: 3817 4359520 [B
  6. 3: 6008 2580208 [I
  7. 4: 24207 2130216 java.lang.reflect.Method
  8. 5: 79367 1904808 java.lang.String
  9. 6: 12306 1374208 java.lang.Class
  10. 7: 39215 1254880 java.util.concurrent.ConcurrentHashMap$Node
  11. 8: 12569 777336 [Ljava.lang.Object;
  12. 9: 17820 570240 java.util.HashMap$Node
  13. 10: 14160 566400 java.util.LinkedHashMap$Entry
  14. 11: 6512 550864 [Ljava.util.HashMap$Node;
  15. 12: 27815 445040 java.lang.Object
  16. 13: 20176 444472 [Ljava.lang.Class;
  17. 14: 7789 436184 java.util.LinkedHashMap
  18. 15: 5890 424080 java.lang.reflect.Field
  19. 16: 197 391320 [Ljava.util.concurrent.ConcurrentHashMap$Node;

如果我们想要打印占用空前最多的前5个对象,可以这么写:

  1. [root@VM_0_5_centos ~]# jmap -histo:live 12771 | head -8
  2. num #instances #bytes class name
  3. ----------------------------------------------
  4. 1: 95096 12216648 [C
  5. 2: 93199 2236776 java.lang.String
  6. 3: 5575 1618464 [B
  7. 4: 13052 1459872 java.lang.Class
  8. 5: 5824 1457256 [I

其中,head -8表示打印结果的从头8行的内容,因为表头和横线还需要占一些空间;所以最终显示的就是有5行内容

结果说明

num: 第一列为行号
instances: 第二列为对象的实例数量
bytes: 第三列为对象占用总大小,单位:字节
class name: 第四列为类的全局限定名,这里需要注意,[C表示char[]数组,[B表示byte[]数组,[I表示int[]数组

3、-clstats 打印类加载信息

  1. jmap -clstats 12771

输入这个命令后需要等一会,我的linux配置比较低,等了一分钟才出来结果;所以这边使用windows系统来执行命令,结果如下
在这里插入图片描述

4、-finalizerinfo 显示在F-Queue队列等待被清理的对象

  1. jmap -finalizerinfo 12771

打印结果如下,可以看到最后一行显示为0,表示当前队列中没有被清理的对象;这是因为垃圾清理过程的速度非常快,是毫秒级别的,所以大部分时间,这个队列里都是没有对象的,

  1. [root@VM_0_5_centos ~]# jmap -finalizerinfo 12771
  2. Attaching to process ID 12771, please wait...
  3. Debugger attached successfully.
  4. Server compiler detected.
  5. JVM version is 25.232-b09
  6. Number of objects pending for finalization: 0

5、-dump<dump-options> 生成堆转储快照

  1. jmap -dump:live,format=b,file=jmap.bin 12771

在执行之前,先说明下这个命令的参数

:live: 只打印存活的对象
format=b:以二进制的格式进行存储
file=jmap.bin:将文件保存到当前目录,文件名称为jmap.bin

打印结果如下

  1. [root@VM_0_5_centos ~]# jmap -dump:live,format=b,file=jmap.bin 12771
  2. Dumping heap to /root/jmap.bin ...
  3. Heap dump file created

这时候堆转储快照就已经生成了,二进制文件已经保存到目录:heap to /root/jmap.bin了。
在这里插入图片描述
因为是二进制文件,所以这里面的信息直接看是看不到的
在这里插入图片描述
这个堆转储文件需要用jhat命令或者jvisualvm 打开,jvisualvm 是图形界面,观看更加直观些,jhat这个工具因为使用起来会启动内置的Http的服务器,生成的结果可在浏览器中观看,但是jhat这个工具现在已经很少使用了,主要原因有2个

  • 1是这个功能比较简陋,因为有了其他更强大的工具来分析,比如jvisualvm
  • 2是一般不会在部署了应用程序的服务器上直接分析dump文件,就算要分析,也会拷贝到其他机器上进行分析,因为分析工作既耗时又耗硬件资源;除非逼不得已,不然不会这么做;

使用jhat打开,命令如下,启动后就会启动内置的服务器,默认端口号:7000;启动后直接在浏览器访问:localhost:7000即可

  1. jhat jmap.bin

在这里插入图片描述

其实jmap是一个不可替代的工具,至少当现在为止还没有出现替代的工具,在线上项目调优时,使用jmap -histo:live pid命令会比较方便查看内存泄漏的原因,阿里巴巴的arthas工具目前也还没有jmap的histo功能;这就是它不可替换的原因;

关键字Java