使用jmap和MAT进行堆内存分析

简介: Java程序运行中常常会遇到各种关于内存的问题,例如内存泄漏、内存溢出、内存使用率太高等问题,如果没有合适的工具和方法,则定位问题时常常感觉难以入手。本文介绍如何使用Jmap配合MAT进行Java堆内存分析,快速定位问题。

引言

Java程序运行中常常会遇到各种关于内存的问题,例如内存泄漏、内存溢出、内存使用率太高等问题,如果没有合适的工具和方法,则定位问题时常常感觉难以入手。本文介绍如何使用Jmap配合MAT进行Java堆内存分析,快速定位问题。

一、使用Jmap获取堆内存信息

1.1 作用

Jmap是Java提供的用于打印进程的堆内存信息的命令,使用这个命令可以查看堆内存的具体使用情况,打印一个进程、可执行core文件、远程debug服务的堆内存,导出堆转储文件,用于离线分析。

1.2 用法

  • jmap [ options ] pid
  • jmap [ options ] executable core
  • jmap [ options ] [ pid ] server-id@ remote-hostname-or-IP

1.3 参数说明

当进程运行于一个64位的虚拟机的话,需要加上-J-d64参数

Option选项:

  • 无可选项
    默认打印共享对象映射信息。
  • -dump:[live,] format=b,file=filename
    打印Java堆内存信息,生成名字为filename,格式为hprof的文件,live参数是可选的,如果指定了则只dump出acitve objects。
  • -finalizerinfo
    打印关于等待结束的对象的信息。
  • -heap
    打印heap的概要信息,GC使用的算法,heap的配置及wise heap的使用情况.
  • -histo[:live]
    打印每个class的实例数目,内存占用,类全名信息. VM的内部类名字开头会加上前缀”*”. 如果live的参数加上后,只统计active objects。
  • -clstats
    打印classloader以及所加载的类的数量以及大小等信息。
  • -F
    当使用jmap -dump或者jamp -histo打印无响应的时候使用-F强制打印。不支持live可选项。
  • -h
    打印帮助信息
  • -help
    打印帮助信息
  • -Jflag
    传递标志给Java虚拟机。

其他参数:

  • pid Java进程ID
  • executable 生成核心转储的java可执行文件
  • core 核心文件
  • remote-hostname-or-IP 远程debug服务器的名称或者IP
  • server-id 唯一ID,假如一台主机上多个远程debug服务,用于区分

1.4 案例

1.4.1 打印heap信息

$ jmap -heap 71664
Attaching to process ID 71664, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.161-b12
using thread-local object allocation.
Parallel GC with 8 thread(s)
Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 27262976 (26.0MB)
   NewSize                  = 8912896 (8.5MB)
   MaxNewSize               = 8912896 (8.5MB)
   OldSize                  = 18350080 (17.5MB)
   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 = 6815744 (6.5MB)
   used     = 2982392 (2.8442306518554688MB)
   free     = 3833352 (3.6557693481445312MB)
   43.75739464393029% used
From Space:
   capacity = 1048576 (1.0MB)
   used     = 0 (0.0MB)
   free     = 1048576 (1.0MB)
   0.0% used
To Space:
   capacity = 1048576 (1.0MB)
   used     = 0 (0.0MB)
   free     = 1048576 (1.0MB)
   0.0% used
PS Old Generation
   capacity = 18350080 (17.5MB)
   used     = 0 (0.0MB)
   free     = 18350080 (17.5MB)
   0.0% used
1779 interned Strings occupying 158984 bytes.

1.4.2 导出堆转储文件

$ jmap -dump:live,format=b,file=71664.hprof 71664

二、模拟内存溢出

为了模拟使用MAT进行堆内存分析地情况,在此特意构建一个内存溢出的情况,导出这个进程的堆转储文件,代码如下:

public class OutOfMemory {
    public static void main(String[] args) throws InterruptedException {
        Vector v=new Vector(5);
        for (int i=1;i<1000000; i++)
        {
            Object o=new Object();
            v.add(o);
        }
        System.out.println("finished");
    }
}

导出堆转储文件,除了以上使用jmap命令以外,也可以使用JVM参数指定当发生OutOfMemory时自动导出堆转储文件。例如以上代码执行时加上以下JVM参数

-Xms25m -Xmx25m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails

执行此程序,导出了堆转储文件java_pid71932.hprof

java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid71932.hprof ...
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
  at java.util.Arrays.copyOf(Arrays.java:3210)
  at java.util.Arrays.copyOf(Arrays.java:3181)
  at java.util.Vector.grow(Vector.java:266)
  at java.util.Vector.ensureCapacityHelper(Vector.java:246)
  at java.util.Vector.add(Vector.java:782)
  at tech.liujintao.leetcode.OutOfMemory.main(OutOfMemory.java:12)
Heap dump file created [12430601 bytes in 0.028 secs]

三、使用MAT分析堆内存

MAT(Memory  Analyzer  Tool)是Eclipse的一个插件,也提供单独运行的版本。主要用于进行堆转储文件的分析,其使用方便简单、功能强大,能够清晰地展示堆内存中各类对象的大小、所占的比例、可能出现内存问题的报表、线程栈等信息,为问题定位提供强大地辅助。

3.1 查看堆内存概要

打开MAT软件,点击"File"->“Open Heap Dump”,选择对应的hprof文件载入堆转储文件,选择Leak Suspect,进入Overview页面

通过上图可以看到当前占用的总的堆内存为6.8M,其中最大的对象占用的内存为6.3M,下面还有多个功能模块:Actions、Reports和Step By Step。

3.2 Leak Suspects

点击Leak Suspects,这个报表是MAT分析出来的可能导致内存泄漏、内存溢出的问题点分析,如下图所示

图上显示,main线程保持了本地变量,这个变量占用了91.64%的堆内存,此对象对应的类是java.lang.Object,由system class loader加载,很明显,此对象占用了那么大的内存并且没有被回收,可能存在问题。

那么是哪个类加载了这个对象呢?这个时候选择点击"See stacktrace"查看栈轨迹

可以看到问题点代码是OutOfMemory.java类第12行,那么就对应查看对应类的代码排查即可。

点击"Details"可以查看更加详细的内容,例如可以查看到问题点代码的最短路径

可以看到java.lang.Object是由main线程中Vector对象保持的。其中Shallow Heap和Retained Heap展示了对象的大小。

  • Shallow Heap是指对象本身堆内存大小,不包含其引用的对象
  • Retained Heap是指当前对象大小+当前对象可直接或间接引用到的对象的大小总和,并且排除被GC Roots直接或者间接引用的对象,可以看作如果对象被GC以后能释放出的堆内存的大小。

Details中还有支配树视图Dominator Tree,用于查看受当前对象支配的对象中哪个占用的Retained Heap比较大,例如下图展示了当前对象“java.lang.Thread @ 0xffc59ab0 main”支配下的对象占用的堆内存情况。

由于加载的对象很多,所以为了方便查看,根据类进行了堆内存的分类

3.3 Histogram视图

该视图以Class类的维度展示每个Class类的实例存在的个数、 占用的Shallow Heap和 Retained Heap 大小,可以用于协助判断哪些实例对象大量驻留于堆内存中,为定位问题提供参考。

更加详细地,可以右键某个对象,选择"List  objects"=> “with outgoing references"或者"with incoming  references”,前者代表的是当前对象引用了哪些对象,后者是当前对象被哪些对象引用了,这就方便进行追踪。

3.4 Group分组视图

在 Histogram视图 和 Domiantor Tree视图时,默认是按照对象的维度进行分组,点击工具栏的分组功能,可以按照类、ClassLoader、包进行分组,更加方便定位到问题代码,具体如下图所示

3.5 Thread视图

Thread视图直观地展示出当前所有的线程,包括线程的名字、线程所占用的堆内存的大小,线程下的本地变量、classloader等信息,具体如下图所示,除了进行内存分析,还支持堆线程的分析,功能相当强大。

通过以上视图,已经能够为堆内存地分析提供极多的参考信息,快速定位内存溢出等问题。

相关文章
|
1月前
|
Web App开发 监控 JavaScript
监控和分析 JavaScript 内存使用情况
【10月更文挑战第30天】通过使用上述的浏览器开发者工具、性能分析工具和内存泄漏检测工具,可以有效地监控和分析JavaScript内存使用情况,及时发现和解决内存泄漏、过度内存消耗等问题,从而提高JavaScript应用程序的性能和稳定性。在实际开发中,可以根据具体的需求和场景选择合适的工具和方法来进行内存监控和分析。
|
23天前
|
缓存 Prometheus 监控
Elasticsearch集群JVM调优设置合适的堆内存大小
Elasticsearch集群JVM调优设置合适的堆内存大小
192 1
|
17天前
|
存储 算法 Java
Java 内存管理与优化:掌控堆与栈,雕琢高效代码
Java内存管理与优化是提升程序性能的关键。掌握堆与栈的运作机制,学习如何有效管理内存资源,雕琢出更加高效的代码,是每个Java开发者必备的技能。
45 5
|
18天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
47 1
|
19天前
|
JavaScript
如何使用内存快照分析工具来分析Node.js应用的内存问题?
需要注意的是,不同的内存快照分析工具可能具有不同的功能和操作方式,在使用时需要根据具体工具的说明和特点进行灵活运用。
38 3
|
1月前
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
|
13天前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
|
21天前
|
Java
JVM内存参数
-Xmx[]:堆空间最大内存 -Xms[]:堆空间最小内存,一般设置成跟堆空间最大内存一样的 -Xmn[]:新生代的最大内存 -xx[use 垃圾回收器名称]:指定垃圾回收器 -xss:设置单个线程栈大小 一般设堆空间为最大可用物理地址的百分之80
|
23天前
|
Java
JVM运行时数据区(内存结构)
1)虚拟机栈:每次调用方法都会在虚拟机栈中产生一个栈帧,每个栈帧中都有方法的参数、局部变量、方法出口等信息,方法执行完毕后释放栈帧 (2)本地方法栈:为native修饰的本地方法提供的空间,在HotSpot中与虚拟机合二为一 (3)程序计数器:保存指令执行的地址,方便线程切回后能继续执行代码
19 3
|
23天前
|
存储 缓存 监控
Elasticsearch集群JVM调优堆外内存
Elasticsearch集群JVM调优堆外内存
44 1