jmap的使用以及内存溢出分析

2年前 (2022) 程序员胖胖胖虎阿
282 0 0

jmap的使用以及内存溢出分析

jmap(java内存映像工具)

jmap(Memory Map for Java)命令用于生成堆转储快照(一般称为heapdump或dump文件)。还有几种方式获取dump文件:使用JVM参数选项-XX:+HeapDumpOnOutOfMemoryError参数,可以让虚拟机在OOM异常出现之后自动生成dump文件,通过-XX:HeapDumpPath=path 设置dump文件路径(有时候dump文件比较大的时候可能无法自动导出,这时候就需要使用jmap -dump手动导出了);通过-XX.+HeapDumpOnCtrlBreak参数则可以使用[Ctrl]+[Break]键让虚拟机生成dump文件;或者在Linux系统下通过Kill -3命令发送进程退出信号,也能拿到dump文件。

jmap的作用并不仅仅是为了获取dump文件,它还可以查询finalize执行队列、Java堆和永久代的详细信息,如空间使用率、当前用的是哪种收集器等。和jinfo命令一样,jmap有不少功能在Windows平台下都是受限的,除了生成dump文件的-dump选项和用于查看每个类的实例、空间占用统计的-histo选项在所有操作系统都提供之外,其余选项都只能在Linux/Solans下使用。官方文档地址

1.1、查看内存使用情况

[root@localhost /]# jmap -heap 9906
Attaching to process ID 9906, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.221-b11

using thread-local object allocation.
Mark Sweep Compact GC

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 989855744 (944.0MB)
   NewSize                  = 20971520 (20.0MB)
   MaxNewSize               = 329908224 (314.625MB)
   OldSize                  = 41943040 (40.0MB)
   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 = 18939904 (18.0625MB)
   used     = 5314648 (5.068443298339844MB)
   free     = 13625256 (12.994056701660156MB)
   28.06058573475346% used
Eden Space:
   capacity = 16842752 (16.0625MB)
   used     = 4780064 (4.558624267578125MB)
   free     = 12062688 (11.503875732421875MB)
   28.38054018725681% used
From Space:
   capacity = 2097152 (2.0MB)
   used     = 534584 (0.5098190307617188MB)
   free     = 1562568 (1.4901809692382812MB)
   25.490951538085938% used
To Space:
   capacity = 2097152 (2.0MB)
   used     = 0 (0.0MB)
   free     = 2097152 (2.0MB)
   0.0% used
tenured generation:
   capacity = 41943040 (40.0MB)
   used     = 36145136 (34.47068786621094MB)
   free     = 5797904 (5.5293121337890625MB)
   86.17671966552734% used

31688 interned Strings occupying 3707776 bytes.

1.2、查看内存中对象数量及大小

#查看所有对象,包括活跃以及非活跃的 
jmap -histo <pid> | more 
#查看活跃对象
jmap -histo:live <pid> | more

[root@localhost /]# jmap -histo:live 9906 | more

 num     #instances         #bytes  class name
----------------------------------------------
   1:         72627       10929400  [C
   2:         12052        2445368  [I
   3:          3709        1718968  [B
   4:         71294        1711056  java.lang.String
   5:         14306        1258928  java.lang.reflect.Method
   6:          8153         922992  java.lang.Class
   7:         26390         844480  java.util.HashMap$Node
   8:         23271         744672  java.util.concurrent.ConcurrentHashMap$Node
   9:         10490         629792  [Ljava.lang.Object;
  10:          5128         536080  [Ljava.util.HashMap$Node;
#对象说明 
B byte 
C char 
D double 
F float 
I int 
J long 
Z boolean 
[数组,如[I表示int[] 
[L+类名 其他对象

1.3、将内存使用情况dump到文件中

#用法: 
jmap -dump:format=b,file=dumpFileName <pid>
#示例 
[root@localhost /]# jmap -dump:format=b,file=/cfile/dump.dat 9906
Dumping heap to /cfile/dump.dat ...
Heap dump file created

jmap的使用以及内存溢出分析

1.4、通过jhat对dump文件进行分析

jvm的内存dump到文件中,这个文件是一个二进制的文件,不方便查看,可以借助于jhat工具进行查看。

#用法: 
jhat -port <port> <file>
#示例
[root@localhost cfile]# jhat -port 8888 /cfile/dump.dat
Reading from /cfile/dump.dat...
Dump file created Thu Aug 19 11:31:02 CST 2021
Snapshot read, resolving...
Resolving 476416 objects...
Chasing references, expect 95 dots...............................................................................................
Eliminating duplicate references...............................................................................................
Snapshot resolved.
Started HTTP server on port 8888
Server is ready.

打开浏览器进行访问:ip+端口(8888)
jmap的使用以及内存溢出分析

在最后面有OQL查询功能。

jmap的使用以及内存溢出分析

进入页面,输入下面语句查询字符串大于10000,点击Execute按钮,查询结果:

select s from java.lang.String s where s.value.length >= 10000

jmap的使用以及内存溢出分析

2、通过MAT工具对dump文件进行分析(官网)

MAT(Memory Analyzer Tool),一个基于Eclipse的内存分析工具,是一个快速、功能丰富的JAVA heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗。使用内存分析工具从众多的对象中进行分析,快速的计算出在内存中对象的占用大小,看看是谁阻止了垃圾收集器的回收工作,并可以通过报表直观的查看到可能造成这种结果的对象。

jmap的使用以及内存溢出分析

2.1、下载安装

jmap的使用以及内存溢出分析

运行独立版 Memory Analyzer 所需的最低 Java 版本是 Java 11 如果本地安装的JDK是更低版本的可以下载旧版本。

jmap的使用以及内存溢出分析

下载完成后解压MemoryAnalyzer-1.8.1.20180910-win32.win32.x86_64.zip,解压后如图

jmap的使用以及内存溢出分析

2.2、使用方式

jmap的使用以及内存溢出分析
jmap的使用以及内存溢出分析
jmap的使用以及内存溢出分析
查看对象以及它的依赖:

jmap的使用以及内存溢出分析

查看可能存在内存泄露的分析:
jmap的使用以及内存溢出分析

3、内存溢出的定位与分析

引起内存溢出的原因有很多种,常见的有以下几种:

  • 内存中加载的数据量过于庞大,如一次从数据库取出过多数据。
  • 集合类中有对对象的引用,使用完后未清空,使得JVM不能回收。
  • 代码中存在死循环或循环产生过多重复的对象实体。
  • 使用的第三方软件中的BUG。
  • 启动参数内存值设定的过小。

内存溢出在实际的生产环境中经常会遇到,如,不断的将数据写入到一个集合中,出现了死循环或者读取超大的文件等等,都可能会造成内存溢出。如果出现了内存溢出,首先我们需要定位到发生内存溢出的环节,并且进行分析,是正常还是非正常情况,如果是正常的需求,就应该考虑加大内存的设置,如果是非正常需求,那么就要对代码进行修改,修复这个bug。

3.1、模拟内存溢出

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class TestJvmOutOfMemory {
    public static void main(String[] args) {
        List<Object> list = new ArrayList<>();
        for (int i = 0; i < 10000000; i++) {
            String str = "";
            for (int j = 0; j < 1000; j++) {
                str += UUID.randomUUID().toString();
            }
            list.add(str);
        }
        System.out.println("ok");
    }
}

为了更快的现实效果,这里使用Idea,设置执行的参数

#在每次发生内存溢出时,JVM会自动将堆转储,dump文件存放在-XX:HeapDumpPath指定的路径下。
-Xms8m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:

jmap的使用以及内存溢出分析

测试结果如下:

jmap的使用以及内存溢出分析
导入到MAT工具中进行分析
jmap的使用以及内存溢出分析
可以看到,有87.02%的内存由Object[]数组占有,所以比较可疑。

分析:已经有超过87%的内存都被它占有,这种情况是非常有可能出现内存溢出的

查看详情:
jmap的使用以及内存溢出分析
可以看到集合中存储了大量的uuid字符串。

版权声明:程序员胖胖胖虎阿 发表于 2022年10月30日 下午10:08。
转载请注明:jmap的使用以及内存溢出分析 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...