您的位置:

jmap命令详解:内存分析利器

一、jmap介绍

jmap是JDK自带的一款用于获取Java应用的heap或类信息快照的命令行工具,它能够让我们更好地了解内存中对象的使用情况,从而方便优化应用程序。

二、jmap常用选项

jmap有多个选项,我们通常会用到以下几个:

-heap

该选项可以让我们获得应用程序的heap大小、占用率等内存信息。例如:

jmap -heap 19321

这条命令会输出进程号19321对应的Java应用的heap信息,例如:

Attaching to process ID 19321, please wait...
Debugger attached successfully.
Server compiler detected
JVM version is 25.102-b14
using thread-local object allocation.
Parallel GC with 4 thread(s)

Heap Configuration:
   MinHeapFreeRatio = 40
   MaxHeapFreeRatio = 70
   MaxHeapSize      = 2147483648 (2048.0MB)  // 最大可用heap大小
   NewSize          = 1048576 (1.0MB)        // 新生代大小
   MaxNewSize       = 33554432 (32.0MB)      // 新生代最大大小
   OldSize          = 4194304 (4.0MB)        // 老年代大小
   NewRatio         = 2
   SurvivorRatio    = 8
   MetaspaceSize    = 21807104 (20.796875MB)  //永久代大小
   CompressedClassSpaceSize = 1073741824 (1024.0MB)  //压缩类空间大小
   MaxMetaspaceSize = 17592186044415 MB
   G1HeapRegionSize = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 279183360 (266.125MB)  // Eden区域容量
   used     = 36645336 (34.925048828125MB)  // 已使用空间
   free     = 242538024 (231.199951171875MB)  // 未使用空间
   13.11123171556415% used //空间使用率
...

-histo[:live]

该选项会输出Java堆内存中的对象信息,包括对象的数量、占用内存大小、类名等。如果加上":live"参数,则只统计存活对象的信息。例如:

jmap -histo 19321

这条命令会输出进程号19321对应的Java应用中对象的统计信息,例如:

num     #instances         #bytes  class name
----------------------------------------------
   1:        183765     474534880  [C  // char数组对象
   2:          8749     274628312  [B  // byte数组对象
   3:         21869     164735880  java.lang.String
   4:          9350      57402584  [I  // int数组对象
   5:        310942      49910872  java.util.LinkedHashMap$Entry
   ...

-dump:[live,]format=b,file=filename

该选项可以生成一个Java堆的dump文件,我们可以用jhat、MAT等工具来解析这个文件,进行一些内存分析。例如:

jmap -dump:live,format=b,file=heapdump.bin 19321

这条命令会生成进程号19321对应Java应用的存活对象的dump文件heapdump.bin。我们可以使用jhat工具来解析这个文件:

jhat heapdump.bin

然后在浏览器中访问http://localhost:7000/即可查看内存分析报告。

三、jmap的优化实践

1. 分析内存泄漏

使用jmap可以针对Java应用程序进行内存泄漏分析。如果应用程序出现了内存泄漏,就可以借助jmap管理Java堆中的对象,通过GC日志、堆转储文件等方式来确定内存泄漏的类型和原因。

下面是一个jmap分析内存泄漏的示例代码:

public class MemoryLeak {

    private static List list = new LinkedList<>();

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 100000; i++) {
            list.add(new Object());
        }
        Thread.sleep(30000);
        list.clear();
        System.out.println("list size: " + list.size());
    }

}


在运行该程序之后,我们可以使用如下命令来获取Java进程的pid:

jps -l

得到pid之后,我们可以使用如下命令来生成堆转储文件:

jmap -dump:live,format=b,file=heapdump.bin pid

使用MAT等工具,可以分析堆转储文件,找到内存泄漏的对象。例如,我们可以在MAT的Histogram视图中查看对象数量排名前100的对象,如下图所示:

可以看到,LinkedList$Entry对象数量排名第一,说明LinkedList中的节点占用了大量的内存。打开该对象的histogram视图,可以查看该对象的实例数量、大小、引用关系等信息,如下图所示:

可以看到,可以直接从该对象的链表、size属性追寻到具体的对象,从而找到内存泄漏的原因。

2. 分析OOM

jmap可以用于定位一个Java进程的OOM问题,步骤与上面类似。首先,需要在发生OOM的时候,使用jmap生成一个堆映射文件。这个文件可以与MAT等工具一起使用,从而进行内存分析。

下面是OOM的一些示例代码:

public class OOMTest {

    private static List list = new LinkedList<>();

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10_000_000; i++) {
            list.add(new Object());
            if (i % 1000 == 0) {
                System.out.println("add object: " + i);
            }
        }
    }

}


在运行该程序之后,我们可以使用如下命令来获取Java进程的pid:

jps -l

得到pid之后,我们可以使用如下命令来生成堆转储文件:

jmap -dump:live,format=b,file=heapdump.bin pid

使用MAT等工具,可以分析堆转储文件,找到OOM的原因。例如,我们可以在MAT的Histogram视图中查看对象数量排名前10的对象,如下图所示:

可以看到,Object实例的数量是最多的,Java应用在运行过程中创建了非常多的Object实例,使得堆内存被耗尽,从而发生了OOM。

四、jmap小结

jmap是一个非常强大的工具,可以帮助我们更好地了解Java应用程序的内存使用情况,从而进行优化。在实践中,我们可以使用jmap分析内存泄漏、OOM等问题,从而根据分析结果对应用程序进行优化。

顶部