jmap以及MAT定位jvm堆内存问题

【针对jdk1.8以后】

获取运行的pid:ps -ef|grep java

或者是 jps

1、jmap命令使用:

常用命令:

(1)jmap -heap pid【查看快要发生FGC】

打印heap的概要信息,GC使用的算法,heap的配置及使用情况,可以用此来判断内存目前的使用情况以及垃圾回收情况

C:\Program Files\Java\jdk1.8.0_172\bin>jmap -heap 4676

Attaching to process ID 4676, please wait...

Debugger attached successfully.

Server compiler detected.

JVM version is 25.172-b11

 

using thread-local object allocation.

Parallel GC with 4 thread(s)

 

Heap Configuration: #堆配置

MinHeapFreeRatio = 0

MaxHeapFreeRatio = 100

MaxHeapSize = 2067791872 (1972.0MB)

NewSize = 42991616 (41.0MB) #新生代分配大小

MaxNewSize = 688914432 (657.0MB)

OldSize = 87031808 (83.0MB) #老年代大小

NewRatio = 2 #老年代与新生代的大小比值

SurvivorRatio = 8 #设置新生代中Eden区与Survivor区的大小比值

MetaspaceSize = 21807104 (20.796875MB)

CompressedClassSpaceSize = 1073741824 (1024.0MB)

MaxMetaspaceSize = 17592186044415 MB

G1HeapRegionSize = 0 (0.0MB)

 

Heap Usage: #堆使用

PS Young Generation

Eden Space:

capacity = 197656576 (188.5MB)

used = 31425808 (29.969985961914062MB)

free = 166230768 (158.53001403808594MB)

15.899196796771387% used

From Space:

capacity = 3670016 (3.5MB)

used = 0 (0.0MB)

free = 3670016 (3.5MB)

0.0% used

To Space:

capacity = 9961472 (9.5MB)

used = 0 (0.0MB)

free = 9961472 (9.5MB)

0.0% used

PS Old Generation

capacity = 105906176 (101.0MB)

used = 27882472 (26.590797424316406MB)

free = 78023704 (74.4092025756836MB)

26.327522202293473% used

28308 interned Strings occupying 3886648 bytes.

 

(2)jmap -histo pid【用来查看大对象】

class的内存情况,主要展示:对象序号、对象实例数、当前对象所占内存的大小、当前对象的全限定名,

默认按照内存字节数降序排列【查看占用内存最多的对象】

#instance 是对象的实例个数 #bytes 是总占用的字节数 class name 对应的就是 Class 文件里的 class 的标识 B 代表 byte C 代表 char D 代表 double F 代表 float I 代表 int J 代表 long Z 代表 boolean 前边有 [ 代表数组, [I 就相当于 int[] 对象用 [L+ 类名表示

 

查看实例数最多的对象:

jmap -histo pid | grep '关键信息' | sort -k 2 -g -r | less

jmap -histo:live pid > a.log 将对象统计信息输出到a.log文件中。此命令执行时,会触发gc,然后再进行统计

 

(3)jmap -dump:live,format=b,file=2019051301.hprof pid

【生成堆转储文件】

JVM会将整个heap的信息dump写入到一个文件,heap如果比较大的话,就会导致这个过程比较耗时,并且执行的过程中为了保证dump的信息是可靠的,所以会暂停应用。

结合MAT进行分析。

 

从安全点日志看,从Heap Dump开始,整个JVM都是停顿的,考虑到IO(虽是写到Page Cache,但或许会遇到background flush),几G的Heap可能产生几秒的停顿,在生产环境上执行时谨慎再谨慎。

live的选项,实际上是产生一次Full GC来保证只看还存活的对象。有时候也会故意不加live选项,看历史对象。

Dump出来的文件建议用JDK自带的VisualVM或Eclipse的MAT插件打开,对象的大小有两种统计方式:

  • 本身大小(Shallow Size):对象本来的大小。
  • 保留大小(Retained Size): 当前对象大小 + 当前对象直接或间接引用到的对象的大小总和。

看本身大小时,占大头的都是char[] ,byte[]之类的,没什么意思(用jmap -histo:live pid 看的也是本身大小)。所以需要关心的是保留大小比较大的对象,看谁在引用这些char[], byte[]。

4、数据分析

这个时候将dump出的文件在ECLIPSE中打开,使用MAT进行分析(ECLIPSE需要先安装MAT插件),会展示如下截图:

jmap以及MAT定位jvm堆内存问题_第1张图片

可以从这个图看出这个类java.lang.ref.Finalizer占用500多M,表示这其中很多不能够被回对象的对象,此时点开hisgogram视图,并通过Retained Heap进行排序,如下截图:

jmap以及MAT定位jvm堆内存问题_第2张图片

从图中可以看出,被线线框圈起来的三个对象占用量非常大,那说明这几个大的对象并没有被释放,那现在就可以有针对性的从代码中去找这几个对象为什么没有被释放了。

再切换到dominator_tree视图:

jmap以及MAT定位jvm堆内存问题_第3张图片

这里可以看到velocity渲染也存在着问题,以及数据库的请求也比较多。

 

总结:

1.如果程序内存不足或者频繁GC,很有可能存在内存泄露情况,这时候就要借助Java堆Dump查看对象的情况。

2.要制作堆Dump可以直接使用jvm自带的jmap命令

3.可以先使用jmap -heap命令查看堆的使用情况,看一下各个堆空间的占用情况。

4.使用jmap -histo:[live]查看堆内存中的对象的情况。如果有大量对象在持续被引用,并没有被释放掉,那就产生了内存泄露,就要结合代码,把不用的对象释放掉。

5.也可以使用 jmap -dump:format=b,file=命令将堆信息保存到一个文件中,再借助jhat命令查看详细内容

6.在内存出现泄露、溢出或者其它前提条件下,建议多dump几次内存,把内存文件进行编号归档,便于后续内存整理分析。

7.在用cms gc的情况下,执行jmap -heap有些时候会导致进程变T,因此强烈建议别执行这个命令,如果想获取内存目前每个区域的使用状况,可通过jstat -gc或jstat -gccapacity来拿到。

 

你可能感兴趣的:(jvm)