jmap
jmap(Java Memory Map)主要用于打印内存映射。常用命令:
jmap -dump:live,format=b,file=xxx.hprof <pid>
查看JVM堆栈的使用情况
[root@localhost ~]# jmap -heap 7243
Attaching to process ID 27900, please wait...
Debugger attached successfully.
Client compiler detected.
JVM version is 20.45-b01
using thread-local object allocation.
Mark Sweep Compact GC
Heap Configuration: #堆内存初始化配置
MinHeapFreeRatio = 40 #-XX:MinHeapFreeRatio设置JVM堆最小空闲比率
MaxHeapFreeRatio = 70 #-XX:MaxHeapFreeRatio设置JVM堆最大空闲比率
MaxHeapSize = 100663296 (96.0MB) #-XX:MaxHeapSize=设置JVM堆的最大大小
NewSize = 1048576 (1.0MB) #-XX:NewSize=设置JVM堆的‘新生代’的默认大小
MaxNewSize = 4294901760 (4095.9375MB) #-XX:MaxNewSize=设置JVM堆的‘新生代’的最大大小
OldSize = 4194304 (4.0MB) #-XX:OldSize=设置JVM堆的‘老生代’的大小
NewRatio = 2 #-XX:NewRatio=:‘新生代’和‘老生代’的大小比率
SurvivorRatio = 8 #-XX:SurvivorRatio=设置年轻代中Eden区与Survivor区的大小比值
PermSize = 12582912 (12.0MB) #-XX:PermSize=<value>:设置JVM堆的‘持久代’的初始大小
MaxPermSize = 67108864 (64.0MB) #-XX:MaxPermSize=<value>:设置JVM堆的‘持久代’的最大大小
Heap Usage:
New Generation (Eden + 1 Survivor Space): #新生代区内存分布,包含伊甸园区+1个Survivor区
capacity = 30212096 (28.8125MB)
used = 27103784 (25.848182678222656MB)
free = 3108312 (2.9643173217773438MB)
89.71169693092462% used
Eden Space: #Eden区内存分布
capacity = 26869760 (25.625MB)
used = 26869760 (25.625MB)
free = 0 (0.0MB)
100.0% used
From Space: #其中一个Survivor区的内存分布
capacity = 3342336 (3.1875MB)
used = 234024 (0.22318267822265625MB)
free = 3108312 (2.9643173217773438MB)
7.001809512867647% used
To Space: #另一个Survivor区的内存分布
capacity = 3342336 (3.1875MB)
used = 0 (0.0MB)
free = 3342336 (3.1875MB)
0.0% used
PS Old Generation: #当前的Old区内存分布
capacity = 67108864 (64.0MB)
used = 67108816 (63.99995422363281MB)
free = 48 (4.57763671875E-5MB)
99.99992847442627% used
PS Perm Generation: #当前的 “持久代” 内存分布
capacity = 14417920 (13.75MB)
used = 14339216 (13.674942016601562MB)
free = 78704 (0.0750579833984375MB)
99.45412375710227% used
新生代内存回收就是采用空间换时间方式;如果from区使用率一直是100% 说明程序创建大量的短生命周期的实例,使用jstat统计jvm在内存回收中发生的频率耗时以及是否有full gc,使用这个数据来评估一内存配置参数、gc参数是否合理。
统计一【jmap -histo】:统计所有类的实例数量和所占用的内存容量
[root@localhost ~]# jmap -histo 7243
num #instances #bytes class name
----------------------------------------------
1: 8969 19781168 [B
2: 1835 2296720 [I
3: 19735 2050688 [C
4: 3448 385608 java.lang.Class
5: 3829 371456 [Ljava.lang.Object;
6: 14634 351216 java.lang.String
7: 6695 214240 java.util.concurrent.ConcurrentHashMap$Node
8: 6257 100112 java.lang.Object
9: 2155 68960 java.util.HashMap$Node
10: 723 63624 java.lang.reflect.Method
11: 49 56368 [Ljava.util.concurrent.ConcurrentHashMap$Node;
12: 830 46480 java.util.zip.ZipFile$ZipFileInputStream
13: 1146 45840 java.lang.ref.Finalizer
......
统计二【jmap -histo】:查看对象数最多的对象,并过滤Map关键词,然后按降序排序输出
[root@localhost ~]# jmap -histo 7243 |grep Map|sort -k 2 -g -r|less
Total 96237 26875560
7: 6695 214240 java.util.concurrent.ConcurrentHashMap$Node
9: 2155 68960 java.util.HashMap$Node
18: 563 27024 java.util.HashMap
21: 505 20200 java.util.LinkedHashMap$Entry
16: 337 34880 [Ljava.util.HashMap$Node;
27: 336 16128 gnu.trove.THashMap
56: 163 6520 java.util.WeakHashMap$Entry
60: 127 6096 java.util.WeakHashMap
38: 127 10144 [Ljava.util.WeakHashMap$Entry;
53: 126 7056 java.util.LinkedHashMap
......
统计三【jmap -histo】:统计实例数量最多的前10个类
[root@localhost ~]# jmap -histo 7243 | sort -n -r -k 2 | head -10
num #instances #bytes class name
----------------------------------------------
Total 96237 26875560
3: 19735 2050688 [C
6: 14634 351216 java.lang.String
1: 8969 19781168 [B
7: 6695 214240 java.util.concurrent.ConcurrentHashMap$Node
8: 6257 100112 java.lang.Object
5: 3829 371456 [Ljava.lang.Object;
4: 3448 385608 java.lang.Class
9: 2155 68960 java.util.HashMap$Node
2: 1835 2296720 [I
统计四【jmap -histo】:统计合计容量最多的前10个类
[root@localhost ~]# jmap -histo 7243 | sort -n -r -k 3 | head -10
num #instances #bytes class name
----------------------------------------------
Total 96237 26875560
1: 8969 19781168 [B
2: 1835 2296720 [I
3: 19735 2050688 [C
4: 3448 385608 java.lang.Class
5: 3829 371456 [Ljava.lang.Object;
6: 14634 351216 java.lang.String
7: 6695 214240 java.util.concurrent.ConcurrentHashMap$Node
8: 6257 100112 java.lang.Object
9: 2155 68960 java.util.HashMap$Node
dump注意事项
-
在应用快要发生FGC的时候把堆数据导出来
老年代或新生代used接近100%时,就表示即将发生GC,也可以再JVM参数中指定触发GC的阈值。
- 查看快要发生FGC使用命令:jmap -heap < pid >
- 数据导出:jmap -dump:format=b,file=heap.bin < pid >
-
通过命令查看大对象:jmap -histo < pid >|less
使用总结
- 如果程序内存不足或者频繁GC,很有可能存在内存泄露情况,这时候就要借助Java堆Dump查看对象的情况
- 要制作堆Dump可以直接使用jvm自带的jmap命令
- 可以先使用
jmap -heap
命令查看堆的使用情况,看一下各个堆空间的占用情况 - 使用
jmap -histo:[live]
查看堆内存中的对象的情况。如果有大量对象在持续被引用,并没有被释放掉,那就产生了内存泄露,就要结合代码,把不用的对象释放掉 - 也可以使用
jmap -dump:format=b,file=<fileName>
命令将堆信息保存到一个文件中,再借助jhat命令查看详细内容 - 在内存出现泄露、溢出或者其它前提条件下,建议多dump几次内存,把内存文件进行编号归档,便于后续内存整理分析
- 在用cms gc的情况下,执行jmap -heap有些时候会导致进程变T,因此强烈建议别执行这个命令,如果想获取内存目前每个区域的使用状况,可通过jstat -gc或jstat -gccapacity来拿到