欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

深入理解JVM系列(第二十一章):JMap命令在JVM调优中的实战应用

最编程 2024-02-10 14:22:31
...

1.Jmap命令

Jmap命令是为了将Java进程当前的内存转存为内存快照,常常用来分析内存溢出或者内存泄漏等问题,执行Jmap命令保存内存快照可以用作后续分析

  • 分析此刻Java堆及方法区的详细信息
  • 分析内存空间使用率
  • 分析当前使用的收集器信息

2.Jamp命令使用

执行Jmap 看下有哪些命令可以使用

PS C:\Users\jzj> jmap -options
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

3.启动程序

下面我们启动Springboot程序,实战以下打印Jamp命令

#JVM参数 启动jar包
java  -XX:+UseG1GC -Xms100M -Xmx100M  -XX:+PrintGC -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:SurvivorRatio=8 -jar .\demo-0.0.1-SNAPSHOT.jar

只有一个简单的Controller

package com.jzj.jvmtest.font;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
public class TestController {
    @GetMapping("/ping")
    private String ping() {
        return "pong";
    }

    @GetMapping("/test")
    private String test(Integer num) {
        try {
            byte[] b = null;
            for (int i = 1; i <= num; i++) {
                //设置 3M的对象
                log.info("======== " + i + "次添加3M对象");
                b = new byte[2 * 1024 * 1024];
                Thread.sleep(3000);

            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }

        return "success";

    }
}

4.jmap实战

4.1 jmap -heap 打印Java堆栈信息

使用 jmap -dump 打印Java堆栈信息必会

  • 打印垃圾回收算法及相信信息
  • 打印堆配置信息
  • 打印堆空间使用信息,包括分代情况,,每个区域的容量信息、当区域已使用内存、可使用内存等等
  • 分代信息中报刊细分的空间的内存使用信息比如S0/S1/Eden等

下面我们实际操作下看下打印信息, 首先先找到进程信息 jps,当前进程pid 9104

# 执行jps,找到进程信息
PS C:\Users\jzj> jps
9104 demo-0.0.1-SNAPSHOT.jar
13996
6476 Jps

执行Jmap -heap 9104

PS C:\Users\jzj> jmap -heap 9104
Attaching to process ID 9104, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.181-b13

using thread-local object allocation.
Garbage-First (G1) GC with 10 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 104857600 (100.0MB)
   NewSize                  = 1363144 (1.2999954223632812MB)
   MaxNewSize               = 62914560 (60.0MB)
   OldSize                  = 5452592 (5.1999969482421875MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 1048576 (1.0MB)

Heap Usage:
G1 Heap:
   regions  = 100
   capacity = 104857600 (100.0MB)
   used     = 44633600 (42.56591796875MB)
   free     = 60224000 (57.43408203125MB)
   42.56591796875% used
G1 Young Generation:
Eden Space:
   regions  = 26
   capacity = 57671680 (55.0MB)
   used     = 27262976 (26.0MB)
   free     = 30408704 (29.0MB)
   47.27272727272727% used
Survivor Space:
   regions  = 8
   capacity = 8388608 (8.0MB)
   used     = 8388608 (8.0MB)
   free     = 0 (0.0MB)
   100.0% used
G1 Old Generation:
   regions  = 9
   capacity = 38797312 (37.0MB)
   used     = 8982016 (8.56591796875MB)
   free     = 29815296 (28.43408203125MB)
   23.15112964527027% used

12508 interned Strings occupying 1091120 bytes.

对打印信息分析

堆信息如下

  • Garbage-First (G1) GC with 10 thread(s) 使用的是G1垃圾收集器
  • MaxHeapSize = 104857600 (100.0MB) 最大堆容量100M,设置的是-Xms
  • NewSize = 1363144 (1.2999954223632812MB) 新生代大小1M
  • MaxNewSize = 62914560 (60.0MB) 最大新生代大小60M
  • OldSize = 5452592 (5.1999969482421875MB) 老年代大小5M
  • NewRatio = 2 新生代:老年代 1:2,也就是新生代1,老年代2,新生代占总比例 1/3
  • SurvivorRatio = 8 S0:S1:Eden 1:1:8, 新生代Eden区占 8/10
  • MetaspaceSize = 21807104 (20.796875MB) 元空间大小 20M

幸存区信息

  • Survivor Space:regions = 8 Survivor分配了8个Region
    • capacity = 8388608 (8.0MB) Survivor容量一共8M
    • used = 8388608 (8.0MB) 已经使用了 8M
    • free = 0 (0.0MB) 空闲的0M
    • 100.0% used 使用率 100%

老年代信息

  • G1 Old Generation: regions = 9 Old区分配了9个Region capacity = 38797312 (37.0MB) Old老年代容量一共37M used = 8982016 (8.56591796875MB) 已经使用 8.5M free = 29815296 (28.43408203125MB) 空闲 28.4M 23.15112964527027% used 老年代使用率 23%
4.2 jmap-dump[:live,]format=b,file=xxx 转存堆快照

生成此刻Java堆的快照信息,转存为dump文件,非常重要,必会

参数详细说明如下

  • live 参数,可选参数,如果添加live,表明只转存堆中的存活对象,没指定live表示转存堆中所有对象
  • format=b 表示以hprof 二进制的格式转存Java堆内存
  • file=xx指定dump文件的文件名
  • -F 强制模式,有可能此刻进程已经假死,强制模式

执行 jmap -dump:format=b,file=xxx111.bin 9104

#执行 dump文件二进制文件
PS C:\Users\jzj> jmap -dump:format=b,file=xxx111.bin 9104
Dumping heap to C:\Users\jzj\xxx111.bin ...
Heap dump file created

xxx111.bin文件已经被创建成功 image.png

执行dump存活对象 jmap -dump:live,format=b,file=xxx111.bin 9104

PS C:\Users\jzj> jmap -dump:live,format=b,file=xxx222.bin 9104
Dumping heap to C:\Users\jzj\xxx222.bin ...
Heap dump file created

xxx222.bin文件已经被创建成功

image.png

执行dump hprof文件jmap -dump:live,format=b,file=dump333.hprof 9104

hprof文件就是堆快照,我们后面要使用工具Jprofiler分析dump文件就是分析该文件

PS C:\Users\jzj> jmap -dump:live,format=b,file=dump333.hprof  9104
Dumping heap to C:\Users\jzj\dump333.hprof ...
Heap dump file created

dump333.hprof 文件被创建成功 image.png

强制执行 jmap -F -dump:live,format=b,file=dump444.hprof 9104

PS C:\Users\jzj> jmap -F -dump:live,format=b,file=dump444.hprof  9104
Attaching to process ID 9104, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.181-b13
Dumping heap to dump444.hprof ...
Heap dump file created

dump444.hprof 文件被创建成功 image.png

4.3 jmap -histo[:live] 显示Java堆统计信息

jmap -histo 显示堆统计信息,包括以下信息

  • 进程中对象的数量
  • 进程占用的内存大小
  • 进程中加载的类信息
  • 指定live参数指统计活动的对象

执行Jmap -histo 9104

PS C:\Users\jzj> jmap -histo 9104
 num     #instances         #bytes  class name
----------------------------------------------
   1:        103267       18015376  [C
   2:          8414        5769720  [I
   3:         10004        5404056  [B
   4:         76092        1826208  java.lang.String
   5:         17108        1127392  [Ljava.lang.Object;
   6:          9203         809864  java.lang.reflect.Method
   7:          6191         683608  java.lang.Class
   8:         19336         618752  java.util.concurrent.ConcurrentHashMap$Node
   9:          6532         523200  [Ljava.util.WeakHashMap$Entry;
  10:          8019         513216  java.net.URL
  11:         24754         511760  [Ljava.lang.Class;
  12:          6478         414592  org.springframework.boot.loader.jar.JarFileWrapper
  13:          3887         330840  [Ljava.util.HashMap$Node;
  14:          7991         319640  java.util.LinkedHashMap$Entry
  15:          6529         313392  java.util.WeakHashMap
  16:          7932         253824  java.util.concurrent.locks.AbstractQueuedSynchronizer$Node
  17:          7767         248544  java.util.HashMap$Node
  18:          7201         230432  java.lang.ref.ReferenceQueue
  19:           129         208976  [Ljava.util.concurrent.ConcurrentHashMap$Node;
  20:          6515         208480  java.util.zip.ZipCoder
  等等等 

执行 jmap -histo:live 9104 统计存活的对象信息

PS C:\Users\jzj> jmap -histo:live 9104

 num     #instances         #bytes  class name
----------------------------------------------
   1:         30300        3043144  [C
   2:         30008         720192  java.lang.String
   3:          6191         683608  java.lang.Class
   4:         18319         586208  java.util.concurrent.ConcurrentHashMap$Node
   5:          6205         546040  java.lang.reflect.Method
   6:          6501         367576  [Ljava.lang.Object;
   7:          2628         319792  [B
   8:          3117         288232  [I
   9:          6449         257960  java.util.LinkedHashMap$Entry
  10:          2902         250488  [Ljava.util.HashMap$Node;
  11:         12433         198928  java.lang.Object
  12:          6134         196288  java.util.HashMap$Node
  13:           110         193312  [Ljava.util.concurrent.ConcurrentHashMap$Node;
  14:          7775         175400  [Ljava.lang.Class;
  15:          2997         167832  java.util.LinkedHashMap
4.4 jmap -clstats 统计类jia'cai

显示Java堆 元空间的类Class的统计信息,对于每个类加载器而言,它的名称、活跃度、地址、父类加载器信息,以及它所加载的类的数量和大小,这些信息全都会打印

主要类信息如下:

  • class_loader:当Java虚拟机运行时,类加载器对象的地址
  • classes:已加载类的数量信息
  • bytes:该类加载器加载的所有类的元数据占用的字节数
  • parent_loader:父类加载器对象的地址,如果没有显示null
  • alive:类是否存活的标识,表示类加载器对象是否将要被垃圾回收,分为dead/live
  • type:该类加载器的类名。

执行 jmap -clstats 9104

PS C:\Users\jzj> jmap -clstats 9104
Attaching to process ID 9104, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.181-b13
finding class loader instances ..done.
computing per loader stat ..done.
please wait.. computing liveness..............................................................................liveness analysis may be inaccurate ...
class_loader    classes bytes   parent_loader   alive?  type

<bootstrap>     1732    3015415   null          live    <internal>
0x00000000fa3cbdb8      1       880     0x00000000f9c0d1b0      dead    sun/reflect/DelegatingClassLoader@0x000000010000a028
0x00000000fa3cd438      1       880     0x00000000f9c0d1b0      dead    sun/reflect/DelegatingClassLoader@0x000000010000a028
0x00000000f9c0dd48      4       4551      null          live    sun/misc/Launcher$ExtClassLoader@0x000000010000fc78
0x00000000fa3c5330      1       1472    0x00000000f9c0d1b0      dead    sun/reflect/DelegatingClassLoader@0x000000010000a028
0x00000000fa3cc3b0      1       1471    0x00000000f9c0d1b0      dead    sun/reflect/DelegatingClassLoader@0x000000010000a028
0x00000000fa3cf9b0      1       1472      null          dead    sun/reflect/DelegatingClassLoader@0x000000010000a028
0x00000000fa3cbc28      1       880     0x00000000f9c0d1b0      dead    sun/reflect/DelegatingClassLoader@0x000000010000a028
0x00000000fa3cd2a8      1       880     0x00000000f9c0d1b0      dead    sun/reflect/DelegatingClassLoader@0x000000010000a028
0x00000000fa3c5120      1       1472    0x00000000f9c0d1b0      dead    sun/reflect/DelegatingClassLoader@0x000000010000a028
0x00000000fa3cc090      1       880     0x00000000f9c0d1b0      dead    sun/reflect/DelegatingClassLoader@0x000000010000a028
0x00000000fa3c5c08      1       1472    0x00000000f9c0d1b0      dead    sun/reflect/DelegatingClassLoader@0x000000010000a028
0x00000000fa3cb908      1       1471    0x00000000f9c0d1b0      dead    sun/reflect/DelegatingClassLoader@0x000000010000a028
0x00000000fa3cc608      1       880     0x00000000f9c0d1b0      dead    sun/reflect/DelegatingClassLoader@0x000000010000a028
0x00000000fa3cbb60      1       880     0x00000000f9c0d1b0      dead    sun/reflect/DelegatingClassLoader@0x000000010000a028
0x00000000fa3cd160      1       880     0x00000000f9c0d1b0      dead    sun/reflect/DelegatingClassLoader@0x000000010000a028
0x00000000f9f1ad90      1       1472      null          dead    sun/reflect/DelegatingClassLoader@0x000000010000a028
0x00000000fa3c4ed8      1       1472    0x00000000f9c0d1b0      dead    sun/reflect/DelegatingClassLoader@0x000000010000a028
0x00000000fa3cc158      1       1474    0x00000000f9c0d1b0      dead    sun/reflect/DelegatingClassLoader@0x000000010000a028
0x00000000fa3c5dd0      1       880     0x00000000f9c0d1b0      dead    sun/reflect/DelegatingClassLoader@0x000000010000a028
0x00000000fa3cb9d0      1       880     0x00000000f9c0d1b0      dead    sun/reflect/DelegatingClassLoader@0x000000010000a028
0x00000000fa3ccf50      1       1471    0x00000000f9c0d1b0      dead    sun/reflect/DelegatingClassLoader@0x000000010000a028
0x00000000f9c0d1b0      3176    5094748 0x00000000f9c0e888      live    org/springframework/boot/loader/LaunchedURLClassLoader@0x0000000100060828
0x00000000fa53fea0      1       880     0x00000000f9c0d1b0      dead    sun/reflect/DelegatingClassLoader@0x000000010000a028
0x00000000fa3c4d48      1       1472      null          dead    sun/reflect/DelegatingClassLoader@0x000000010000a028
0x00000000fa3cb240      1       1472    0x00000000f9c0d1b0      dead    sun/reflect/DelegatingClassLoader@0x000000010000a028
0x00000000fa3cc540      1       880     0x00000000f9c0d1b0      dead    sun/reflect/DelegatingClassLoader@0x000000010000a028
0x00000000fa3cfb40      1       1472    0x00000000f9c0d1b0      dead    sun/reflect/DelegatingClassLoader@0x000000010000a028

total = 51      5001    8243960     N/A         alive=6, dead=45            N/A

至此 ,我们已经学习了Jmap的命令,比较常用的就是 jmap-heap堆栈信息及 jmap-dump保存堆栈快照,一定要会用这两个,特别是 jmap-dump 这个命令是分析内存 dump文件的必经之路

推荐阅读