第一篇介绍 jps 工具的文章中已经介绍了如果找到我们所需要的Java进程,第二篇 jinfo 工具的文章中介绍了如何查看指定的Java进程的系统配置信息和用户参数信息,本文将更深入一步,介绍如何使用jmap工具在程序运行期间动态查看堆使用信息,以及Java对象信息。

jmap适用场景

jmap一般使用于以下的几种情况:

  • 内存泄漏,线上程序在运行一段时间之后内存越来越大,这是我们要使用jmap命令dump出,内存的对象信息,然后进行分析
  • 内存使用大于预期,这个一般是程序设计不合理,有很多冗余的对象放置在内存中,可以使用jmap查看内存中的对象,看看有些对象是否必要
  • jvm调优,可以使用jmap查看整个堆的使用情况。根据新生代,老年代的大小和使用比利,来对各个区域进行设置。

jmap使用详解

可以使用jmap -h命令来查看jmap所支持的参数选项

jmap

不带任何选项参数的jmap,将会打印目标虚拟机中加载的共享对象的起始地址,映射大小以及共享对象的文件的完整路径名称,如下图所示:

jmpa -heap

打印堆的摘要信息,包括GC算法,堆的配置信息和使用信息,如下图所示:

参数说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
Heap Configuration: ##堆配置信息
MinHeapFreeRatio = 40 ##在堆的使用率小于40%的时候进行收缩,当Xmx=Xms的时候此配置无效
MaxHeapFreeRatio = 70 ##在堆使用率大于70%的时候进行扩展,当Xmx=Xms的时候此配置无效
MaxHeapSize = 4294967296 (4096.0MB) ##堆的最大空间
NewSize = 1073741824 (1024.0MB) ##新生代的大小
MaxNewSize = 1073741824 (1024.0MB) ##最大的新生代的大小
OldSize = 2147483648 (2048.0MB) ##老年代的大小
NewRatio = 2 ##新生代中Eden和和Survivor区的比例为 8:1:1
SurvivorRatio = 8 ##新生代中Eden和和Survivor区的比例为 8:1:1
PermSize = 134217728 (128.0MB) ##永久代的大小
MaxPermSize = 134217728 (128.0MB) ##永久代的最大内存
G1HeapRegionSize = 0 (0.0MB) ##使用G1垃圾收集的区间
Heap Usage: ## 堆的使用信息
New Generation (Eden + 1 Survivor Space): ##新生代的大小(Eden区加一个Survivor区的空间信息)
capacity = 966393856 (921.625MB) ##总内存
used = 133592616 (127.40384674072266MB) ##已使用内存
free = 832801240 (794.2211532592773MB) ##剩余内存
13.823827124993642% used ## 使用内存占比
Eden Space: ## Eden区的大小
capacity = 859045888 (819.25MB)
used = 130388888 (124.3485336303711MB)
free = 728657000 (694.9014663696289MB)
15.178337946947952% used
From Space: ##第一个Surivivor区的空间信息
capacity = 107347968 (102.375MB)
used = 3203728 (3.0553131103515625MB)
free = 104144240 (99.31968688964844MB)
2.984432830624237% used
To Space: ##第二个Survivor区的空间信息
capacity = 107347968 (102.375MB)
used = 0 (0.0MB)
free = 107347968 (102.375MB)
0.0% used
concurrent mark-sweep generation: ##CMS垃圾收集占用的空间信息
capacity = 3221225472 (3072.0MB)
used = 2427667128 (2315.203788757324MB)
free = 793558344 (756.7962112426758MB)
75.36470666527748% used
Perm Generation: ##永久代的空间信息
capacity = 134217728 (128.0MB)
used = 63876264 (60.917152404785156MB)
free = 70341464 (67.08284759521484MB)
47.5915253162384% used

jmap -histo

按照类进行归纳,该命令会统计Java进程内每个类的对象个数之和,和该类所有对象的占用的空间之和,以及类名称。注意:
      1 该命令在线上执行的时候要做好评估,否则会导致线上机器宕机。
      2 jmap -histo:live 这个命令执行,JVM会先触发gc,然后再统计信息。


通过上图可以看到本进程内,出现最多的类是char数组,它一共有3398822个对象实例,这些对象总共占用278200648个字节。

jmap -dump

jamp -dump命令主要是用来dump Java进程的堆内容,然后配合jhat命令,使用OQL语言进行内存对象的分析。下面将详细分析这个过程

dump内存文件

执行如下的命令在当前目录下dump出内存的二进制文件。

1
sudo jmap -F -dump:format=b,file=heapDump 22575

执行这个命令,会将JVM的整个heap信息dump到一个文件之中,如果heap比较大,会比较耗时,而且会导致显示服务不可用。所以在成产环境要慎重使用。

执行完成之后会在当前目录下面生成一个文件名为heapDump的二进制文件。

jhat解析内存dump二进制文件

执行如下的命令,分析dump出来二进制内存文件,并启动一个webserver。

1
sudo jhat heapDump

OQL分析dump文件

在浏览器输入:

1
http://10.86.43.50:7000/ ## IP地址换成你机器的IP就行了。


下面就可以查询dump信息,也可以使用OQL语言进行查询。(具体OQL)语法参见Oracle官方教程