4.3 jmap与jhat

2017-02-14 13:50:17 8,207 0

jmap(Java Memory Map)命令可以获得运行中的jvm的堆的快照,从而可以离线分析堆,以检查内存泄漏,检查一些严重影响性能的大对象的创建,检查系统中什么对象最多,各种对象所占内存的大小。

命令语法:

jmap [option] <pid>

其中pid可以通过jps命令获取,option可选项如下: 

-heap:打印jvm 内存整体使用情况 

-histo[:live]:打印jvm heap的直方图。其输出信息包括类名,对象数量,对象占用大小。 

-dump:<dump-options>:dump java内存到二进制文件中。

   dump-options: 

    live       是否只dump当前存活的对象,如果不指定,将会dump所有的对象,包括待回收的对象。

    format=b    文件的格式

    file=<file>  指定文件的输出位置

-permstat:打印permanent generation heap情况 

上述命令中的-histo和-dumo都包含一个live选项,如果指定live,在统计前会进行full gc,因此不加live的堆大小要大于加live堆的大小。

命令使用

1、打印jvm 内存整体使用情况

jmap -heap pid  

可以观察到New Generation(Eden Space,From Space,To Space),tenured generation,Perm Generation的内存使用情况


[root@www wangxiaoxiao]# jmap -heap 12446

Attaching to process ID 12446, please wait...

Debugger attached successfully.

Server compiler detected.

JVM version is 25.65-b01


using parallel threads in the new generation.

using thread-local object allocation.

Concurrent Mark-Sweep GC


Heap Configuration:

   MinHeapFreeRatio         = 40

   MaxHeapFreeRatio         = 70

   MaxHeapSize              = 536870912 (512.0MB)

   NewSize                  = 134152192 (127.9375MB)

   MaxNewSize               = 134217728 (128.0MB)

   OldSize                  = 65536 (0.0625MB)

   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 = 120782848 (115.1875MB)

   used     = 80598816 (76.86502075195312MB)

   free     = 40184032 (38.322479248046875MB)

   66.73034899789745% used

Eden Space:

   capacity = 107413504 (102.4375MB)

   used     = 75663840 (72.15866088867188MB)

   free     = 31749664 (30.278839111328125MB)

   70.44164577295606% used

From Space:

   capacity = 13369344 (12.75MB)

   used     = 4934976 (4.70635986328125MB)

   free     = 8434368 (8.04364013671875MB)

   36.91262637867647% used

To Space:

   capacity = 13369344 (12.75MB)

   used     = 0 (0.0MB)

   free     = 13369344 (12.75MB)

   0.0% used

concurrent mark-sweep generation:

   capacity = 198795264 (189.5859375MB)

   used     = 138417264 (132.00498962402344MB)

   free     = 60378000 (57.58094787597656MB)

   69.62804908672271% used


44470 interned Strings occupying 5924248 bytes.

2、查看类名,对象数量,对象占用大小

jmap -histo[:live] pid 

可以观察heap中所有对象的情况(heap中所有生存的对象的情况)。包括对象数量和所占空间大小。 


[root@www wangxiaoxiao]# jmap -histo 12446 | more


 num     #instances         #bytes  class name

----------------------------------------------

   1:        648278       81563800  [C

   2:        252059       30068112  [B

   3:        502067       12049608  java.lang.String

   4:         53315        8825728  [I

   5:        114705        8823824  [Ljava.lang.Object;

   6:        210107        6723424  java.util.HashMap$Node

   7:         60821        5352248  java.lang.reflect.Method

   8:         62414        5060664  [S

   9:         54688        4812544  java.util.jar.JarEntry

  10:        131308        4201856  org.springframework.boot.loader.util.AsciiBytes

  11:         28337        3420512  [Ljava.util.HashMap$Node;

  12:         59001        3304056  org.springframework.boot.loader.jar.JarEntryData

  13:         87337        2794784  java.util.concurrent.ConcurrentHashMap$Node

  14:         69859        2794360  java.lang.ref.SoftReference

  15:         36298        2613456  java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask

  16:         22683        2497536  java.lang.Class

  17:         60280        2411200  java.util.LinkedHashMap$Entry

  18:         27206        1741184  java.net.URL

  19:         56643        1359432  java.util.concurrent.ConcurrentSkipListMap$Node

  20:         22687        1270472  java.util.LinkedHashMap

  21:         23984        1151232  java.util.HashMap

  22:         15122        1088784  org.springframework.boot.loader.jar.JarURLConnection

  23:         26521        1060840  java.util.TreeMap$Entry

  24:         57292         916672  java.lang.Object

  25:         27960         894720  java.util.Hashtable$Entry

  26:         36242         869808  java.util.concurrent.Executors$RunnableAdapter

  27:           931         838160  [Ljava.util.concurrent.ConcurrentHashMap$Node;

输出说明:

instances列:表示当前类有多少个实例。

bytes列:说明当前类的实例总共占用了多少个字节

class name列:表示的就是当前类的名称,class name 解读:

B代表byte 

C代表char 

D代表double 

F代表float 

I代表int 

J代表long 

Z代表boolean 

前边有[代表数组,[I 就相当于int[] 

对象用[L+类名表示 

手工dump内存到本地文件

jmap -dump:file=heap.map  12446

[root@www wangxiaoxiao]# jmap -dump:file=heap.map  12446

Dumping heap to /home/wangxiaoxiao/heap.map ...

Heap dump file created

jhat(Java Heap Analyse Tool)

用途:是用来分析java堆的命令,可以将堆中的对象以html的形式显示出来,包括对象的数量,大小等等,并支持对象查询语言

使用:

[root@www wangxiaoxiao]# jhat heap.map #heap.map是通过jmap -dump:file=heap.map pid命令导出

Reading from heap.map...

Dump file created Tue Feb 14 10:50:34 CST 2017

Snapshot read, resolving...

Resolving 2570075 objects...

Chasing references, expect 514 dots

...........................................................................................................................

................................................................................................................

Eliminating duplicate references

.............................................................................................................................

................................................................................................................

.................................................................................................................

Snapshot resolved.

Started HTTP server on port 7000

Server is ready.

访问 http://localhost:7000,就可以查看详细的内存信息

关于jhat,不打算多说,因为一般实际开发中,很少使用jhat来直接对内存dump文件进行分析。下面介绍一个MAT,这个用的比较多。

MAT(Memory Analyzer Tool)

MAT是一个eclipse的插件,上手起来比较快。它能够快速的分析dump文件,可以直观的看到各个对象在内存占用的量大小,以及类实例的数量,对象之间的引用关系,找出对象的GC Roots相关的信息,此外还能生成内存泄露报表,疑似泄露大对象的报表等等。

安装MAT


可以选择eclipse插件的方式安装

http://download.eclipse.org/mat/1.3/update-site/

也可以选择单独MAT程序下载安装

http://www.eclipse.org/mat/downloads.php


内存溢出时,自动保存dump文件

前面是手工导出内存dump映射文件,如果应用已经在线上运行,为了能获取应用出现内存溢出时获得heap dump文件,以便在之后分析,可以在JVM启动时指定参数:-XX:+HeapDumpOnOutOfMemoryError,JVM会在遇到OutOfMemoryError时保存一个“堆转储快照”,并将其保存在一个文件中。 文件路径通过-XX:HeapDumpPath指定。

案例

public class OomDemo {
    public static void main(String[] args) {
        Map<String,Long> map=new HashMap<String, Long>();
        for (long i = 0; i < Long.MAX_VALUE; i++) {
            map.put(i+"",i);
        }
    }
}

设置虚拟机参数为:-Xmx40m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\Java\dump.hprof

执行程序,很快会抛出异常:

java.lang.OutOfMemoryError: GC overhead limit exceeded

Dumping heap to D:\Java\dump ...

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded

Heap dump file created [55940678 bytes in 0.556 secs]

at java.lang.StringBuilder.toString(StringBuilder.java:405)

at oom.OomDemo.main(OomDemo.java:13)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:606)

at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

生成了文件之后,就可以通过MAT打开来进行分析

File->Open Heap Dump->选择文件

弹出如下界面:

QQ截图20170214134508.png

选中第一个,点击finish,出现以下界面:

QQ截图20170214134727.png

可以看到,提示在主线程(main)的一个本地变量中使用了98.77%的内存,而这个本地变量就是java.util.HashMap$Entry[]。


上一篇:4.2 jinfo 下一篇:4.4 jstat