欢迎您, 来到 宁时修博客.^_^

Java程序线上排查命令----03、jmap

2019/06/19 言则行 Java,linux命令 495
Java程序的问题排查命令

一、jmap的简介

    jmap是JDK自带的工具软件,主要用于打印指定Java进程、核心文件或远程调试服务器的共享对象内存映射或堆内存详细信息。可以使用jmap生成Heap Dump。

    官方文档:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jmap.html


   

    什么是堆Dump?

    堆Dump是反应Java堆使用情况的内存镜像,其中主要包括系统信息、虚拟机属性、完整的线程Dump、所有类和对象的状态等。 一般,在内存不足、GC异常等情况下,我们就会怀疑有内存泄露。这个时候我们就可以制作堆Dump来查看具体情况,分析原因。


    常见内存错误:

    outOfMemoryError 年老代内存不足。

    outOfMemoryError: PermGen Space 永久代内存不足(java 6)。

    outOfMemoryError: Metaspace 元空间内存不足(java 8)。

    outOfMemoryError: GC overhead limit exceed 垃圾回收时间占用系统运行时间的98%或以上。



二、jmap使用参数

[root@test-web-server ~]#  jmap -help
Usage:
    jmap [option] <pid>
        (to connect to running process)
    jmap [option] <executable <core>
        (to connect to a core file)
    jmap [option] [server_id@]<remote server IP or hostname>
        (to connect to remote debug server)

where <option> is one of:
    <none>               to print same info as Solaris pmap
    -heap                to print java heap summary
    -histo[:live]        to print histogram of java object heap; if the "live"
                         suboption is specified, only count live objects
    -clstats             to print class loader statistics
    -finalizerinfo       to print information on objects awaiting finalization
    -dump:<dump-options> to dump java heap in hprof binary format
                         dump-options:
                           live         dump only live objects; if not specified,
                                        all objects in the heap are dumped.
                           format=b     binary format
                           file=<file>  dump heap to <file>
                         Example: jmap -dump:live,format=b,file=heap.bin <pid>
    -F                   force. Use with -dump:<dump-options> <pid> or -histo
                         to force a heap dump or histogram when <pid> does not
                         respond. The "live" suboption is not supported
                         in this mode.
    -h | -help           to print this help message
    -J<flag>             to pass <flag> directly to the runtime system


    参数解释:

    option: 选项参数。

    pid: 需要打印配置信息的进程ID。

    executable: 产生核心dump的Java可执行文件。

    core: 需要打印配置信息的核心文件。

    server-id:可选的唯一id,如果相同的远程主机上运行了多台调试服务器,用此选项参数标识服务器。

    remote server IP or hostname :远程调试服务器的IP地址或主机名。



    <none>: 如果使用不带选项参数的jmap打印共享对象映射,将会打印目标虚拟机中加载的每个共享对象的起始地址、映射大小以及共享对象文件的路径全称。这与Solaris的pmap工具比较相似。

    -heap: 打印堆的摘要信息,包括使用的GC算法、堆配置信息和generation wise heap usage。

    -histo[:live]: 打印堆的柱状图。其中包括每个Java类、对象数量、内存大小(单位:字节)、完全限定的类名。打印的虚拟机内部的类名称将会带有一个 ' * ’前缀。如果指定了live子选项,则只计算活动的对象。

    -clstats:打印类加载器统计信息。

    -finalizerinfo: 显示在F-Queue队列等待Finalizer线程执行finalizer方法的对象。

    -dump:<dump-options>:用hprof二进制格式生成堆转储快照。

                        dump-options:

                            live :仅实时转储活动对象;如果未指定,则转储堆中的所有对象。

                            format=b :二进制格式。

                            file=<file> :将堆信息转储到哪个文件。

                        示例: jmap -dump:live,format=b,file=heap.bin <pid>

    -F: 强制模式。与 -dump:<dump options><pid>或 -histo 一起使用在 <pid> 没有响应时以强制堆转储或柱状图,此模式不支持"live"子选项。

    -h | -help:打印帮助信息。

    -J<flag>:指定传递给运行jmap的JVM的参数。




三、jmap使用示例

    示例1:no option

    命令:jmap pid

[root@test-web-server ~]# jmap 31941
Attaching to process ID 31941, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.131-b11
0x0000000000400000	7K	/usr/java/jdk1.8.0_131/jre/bin/java
0x00007f09c49d2000	86K	/usr/lib64/libgcc_s-4.8.5-20150702.so.1
0x00007f09c4be8000	250K	/usr/java/jdk1.8.0_131/jre/lib/amd64/libsunec.so
0x00007f09c52c5000	90K	/usr/java/jdk1.8.0_131/jre/lib/amd64/libnio.so
0x00007f09e0472000	49K	/usr/java/jdk1.8.0_131/jre/lib/amd64/libmanagement.so
0x00007f09e067b000	103K	/usr/lib64/libresolv-2.17.so
0x00007f09e0894000	30K	/usr/lib64/libnss_dns-2.17.so
0x00007f09e0e9c000	113K	/usr/java/jdk1.8.0_131/jre/lib/amd64/libnet.so
0x00007f0a17590000	121K	/usr/java/jdk1.8.0_131/jre/lib/amd64/libzip.so
0x00007f0a177ab000	60K	/usr/lib64/libnss_files-2.17.so
0x00007f0a179be000	220K	/usr/java/jdk1.8.0_131/jre/lib/amd64/libjava.so
0x00007f0a17bea000	64K	/usr/java/jdk1.8.0_131/jre/lib/amd64/libverify.so
0x00007f0a17df8000	42K	/usr/lib64/librt-2.17.so
0x00007f0a1c050000	1110K	/usr/lib64/libm-2.17.so
0x00007f0a1c352000	16597K	/usr/java/jdk1.8.0_131/jre/lib/amd64/server/libjvm.so
0x00007f0a1d345000	2101K	/usr/lib64/libc-2.17.so
0x00007f0a1d712000	18K	/usr/lib64/libdl-2.17.so
0x00007f0a1d916000	99K	/usr/java/jdk1.8.0_131/jre/lib/amd64/jli/libjli.so
0x00007f0a1db2c000	138K	/usr/lib64/libpthread-2.17.so
0x00007f0a1dd48000	159K	/usr/lib64/ld-2.17.so



    示例2:heap

    命令:jmap -heap pid

[root@test-web-server ~]# jmap -heap 31941
Attaching to process ID 31941, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.131-b11

using parallel threads in the new generation.  # 在新生代中使用并行线程。
using thread-local object allocation.          # 使用本地线程对象分配。
Concurrent Mark-Sweep GC         # GC 方式

Heap Configuration:              # 堆内存初始化配置
   MinHeapFreeRatio         = 40 # 对应jvm启动参数-XX:MinHeapFreeRatio设置JVM堆最小空闲比率(default 40)
   MaxHeapFreeRatio         = 70 # 对应jvm启动参数 -XX:MaxHeapFreeRatio设置JVM堆最大空闲比率(default 70)
   MaxHeapSize              = 6815744000 (6500.0MB)  # 对应jvm启动参数-XX:MaxHeapSize=设置JVM堆的最大大小
   NewSize                  = 2621440000 (2500.0MB)  # 对应jvm启动参数-XX:NewSize=设置JVM堆的‘新生代’的初始大小
   MaxNewSize               = 2621440000 (2500.0MB)  # 对应jvm启动参数-XX:MaxNewSize=设置JVM堆的‘新生代’的最大大小
   OldSize                  = 4194304000 (4000.0MB)  # 对应jvm启动参数-XX:OldSize=<value>:设置JVM堆的‘老生代’的大小
   NewRatio                 = 2                      # 对应jvm启动参数-XX:NewRatio=:‘新生代’和‘老生代’的大小比率
   SurvivorRatio            = 6                      # 对应jvm启动参数-XX:SurvivorRatio=设置年轻代中Eden区与Survivor区的大小比值 
   MetaspaceSize            = 805306368 (768.0MB)    # 对应jvm启动参数-XX:MetaspaceSize=<value>:设置JVM堆的‘元空间’的初始大小
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 805306368 (768.0MB)    # 对应jvm启动参数-XX:MaxMetaspaceSize=<value>:设置JVM堆的‘元空间’的最大大小
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:                                      # 堆内存使用情况
New Generation (Eden + 1 Survivor Space):        # 新生代(Eden区加 1个Survivor空间)
   capacity = 2293760000 (2187.5MB)              # 新生代内存总量
   used     = 752786104 (717.9127731323242MB)    # 新生代已使用
   free     = 1540973896 (1469.5872268676758MB)  # 新生代剩余容量
   32.81886962890625% used                       # 新生代使用比率
Eden Space:                                      # Eden区内存分布
   capacity = 1966080000 (1875.0MB)              # Eden区总容量
   used     = 752786104 (717.9127731323242MB)    # Eden区已使用
   free     = 1213293896 (1157.0872268676758MB)  # Eden区剩余容量
   38.288681233723956% used                      # Eden区使用比率
From Space:                                      # Survivor 0 区的内存分布
   capacity = 327680000 (312.5MB)                 
   used     = 0 (0.0MB)
   free     = 327680000 (312.5MB)
   0.0% used
To Space:                                        # Survivor 1 区的内存分布
   capacity = 327680000 (312.5MB)
   used     = 0 (0.0MB)
   free     = 327680000 (312.5MB)
   0.0% used
concurrent mark-sweep generation:           # 老生代(使用CMS垃圾收集器)内存分布
   capacity = 4194304000 (4000.0MB)
   used     = 65880488 (62.828529357910156MB)
   free     = 4128423512 (3937.17147064209MB)
   1.5707132339477539% used

42809 interned Strings occupying 5190576 bytes.



    示例3:histo[:live]

    命令:jmap -histo pid

    jmap -histo:live 这个命令执行,JVM会先触发gc,然后再统计信息。

[root@test-web-server ~]# jmap -histo 20758
 num     #instances         #bytes  class name (module)
 编号     个数                字节     类名
-------------------------------------------------------
   1:       4706381      377737584  [B (java.base@12.0.1)
   2:       3019638       72471312  java.lang.String (java.base@12.0.1)
   3:        926428       66702816  java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask (java.base@12.0.1)
   4:       2072905       66332960  java.util.HashMap$Node (java.base@12.0.1)
   5:        421977       41532152  [J (java.base@12.0.1)
   6:       1244222       39815104  java.util.concurrent.atomic.LongAdder (java.base@12.0.1)
   7:        980475       39219000  java.util.TreeMap$Entry (java.base@12.0.1)
   8:        683071       32787408  java.util.HashMap (java.base@12.0.1)
   9:        507936       28444416  org.apache.lucene.index.FieldInfo
  10:        192560       28149264  [Ljava.lang.Object; (java.base@12.0.1)
.......................................................
Total      13047300      793911000



[root@test-web-server ~]# jmap -histo:live 20758
 num     #instances         #bytes  class name (module)
 编号     个数                字节     类名
-------------------------------------------------------
   1:       1723529      239049880  [B (java.base@12.0.1)
   2:       1062137       33988384  java.util.HashMap$Node (java.base@12.0.1)
   3:       1173210       28157040  java.lang.String (java.base@12.0.1)
   4:        168180       23418152  [J (java.base@12.0.1)
   5:        463145       18525800  java.util.TreeMap$Entry (java.base@12.0.1)
   6:        367917       17660016  java.util.HashMap (java.base@12.0.1)
   7:        530638       16980416  java.util.concurrent.atomic.LongAdder (java.base@12.0.1)
   8:        104888       12313216  [Ljava.util.HashMap$Node; (java.base@12.0.1)
.......................................................
Total      10910201      600065680



    示例4:dump:<dump-options>

    命令:jmap -dump:format=b,file=heapdump.phrof pid

    将内存使用的详细情况输出到文件,然后用jhat命令可以参看 jhat -port 5000 heapDump 在浏览器中访问:http://localhost:5000/ 查看详细信息。

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



    示例5:clstats

    命令:jmap -clstats pid

    -clstats是 -permstat的替代方案,在JDK8之前,-permstat用来打印类加载器的数据,打印Java堆内存的永久保存区域的类加载器的智能统计信息。对于每个类加载器而言,它的名称、活跃度、地址、父类加载器、它所加载的类的数量和大小都会被打印。此外,包含的字符串数量和大小也会被打印。

    谨慎使用,会对进程有影响,比如卡住。



    示例6:finalizerinfo

    命令:jmap -finalizerinfo pid

    打印等待终结的对象信息。

    Number of objects pending for finalization: 0    说明当前F-QUEUE队列中并没有等待Fializer线程执行finalizer方法的对象


    

四、总结

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

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

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

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

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

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


点赞
说说你的看法

所有评论: (0)

# 加入组织

1、用手机QQ扫左侧二维码

2、搜Q群:1058582137

3、点击 宁时修博客交流群