使用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等信息,具体如下图所示,除了进行内存分析,还支持堆线程的分析,功能相当强大。

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

相关文章
|
8月前
|
存储 弹性计算 缓存
阿里云服务器ECS经济型、通用算力、计算型、通用和内存型选购指南及使用场景分析
本文详细解析阿里云ECS服务器的经济型、通用算力型、计算型、通用型和内存型实例的区别及适用场景,涵盖性能特点、配置比例与实际应用,助你根据业务需求精准选型,提升资源利用率并降低成本。
549 3
|
4月前
|
设计模式 缓存 Java
【JUC】(4)从JMM内存模型的角度来分析CAS并发性问题
本篇文章将从JMM内存模型的角度来分析CAS并发性问题; 内容包含:介绍JMM、CAS、balking犹豫模式、二次检查锁、指令重排问题
153 1
|
7月前
|
存储 人工智能 自然语言处理
AI代理内存消耗过大?9种优化策略对比分析
在AI代理系统中,多代理协作虽能提升整体准确性,但真正决定性能的关键因素之一是**内存管理**。随着对话深度和长度的增加,内存消耗呈指数级增长,主要源于历史上下文、工具调用记录、数据库查询结果等组件的持续积累。本文深入探讨了从基础到高级的九种内存优化技术,涵盖顺序存储、滑动窗口、摘要型内存、基于检索的系统、内存增强变换器、分层优化、图形化记忆网络、压缩整合策略以及类操作系统内存管理。通过统一框架下的代码实现与性能评估,分析了每种技术的适用场景与局限性,为构建高效、可扩展的AI代理系统提供了系统性的优化路径和技术参考。
457 4
AI代理内存消耗过大?9种优化策略对比分析
|
11月前
|
存储 Java
课时4:对象内存分析
接下来对对象实例化操作展开初步分析。在整个课程学习中,对象使用环节往往是最棘手的问题所在。
107 4
|
11月前
|
Java 编译器 Go
go的内存逃逸分析
内存逃逸分析是Go编译器在编译期间根据变量的类型和作用域,确定变量分配在堆上还是栈上的过程。如果变量需要分配在堆上,则称作内存逃逸。Go语言有自动内存管理(GC),开发者无需手动释放内存,但编译器需准确分配内存以优化性能。常见的内存逃逸场景包括返回局部变量的指针、使用`interface{}`动态类型、栈空间不足和闭包等。内存逃逸会影响性能,因为操作堆比栈慢,且增加GC压力。合理使用内存逃逸分析工具(如`-gcflags=-m`)有助于编写高效代码。
239 2
|
7月前
|
存储
阿里云轻量应用服务器收费标准价格表:200Mbps带宽、CPU内存及存储配置详解
阿里云香港轻量应用服务器,200Mbps带宽,免备案,支持多IP及国际线路,月租25元起,年付享8.5折优惠,适用于网站、应用等多种场景。
2414 0
|
7月前
|
存储 缓存 NoSQL
内存管理基础:数据结构的存储方式
数据结构在内存中的存储方式主要包括连续存储、链式存储、索引存储和散列存储。连续存储如数组,数据元素按顺序连续存放,访问速度快但扩展性差;链式存储如链表,通过指针连接分散的节点,便于插入删除但访问效率低;索引存储通过索引表提高查找效率,常用于数据库系统;散列存储如哈希表,通过哈希函数实现快速存取,但需处理冲突。不同场景下应根据访问模式、数据规模和操作频率选择合适的存储结构,甚至结合多种方式以达到最优性能。掌握这些存储机制是构建高效程序和理解高级数据结构的基础。
792 1
|
7月前
|
存储 弹性计算 固态存储
阿里云服务器配置费用整理,支持一万人CPU内存、公网带宽和存储IO性能全解析
要支撑1万人在线流量,需选择阿里云企业级ECS服务器,如通用型g系列、高主频型hf系列或通用算力型u1实例,配置如16核64G及以上,搭配高带宽与SSD/ESSD云盘,费用约数千元每月。
806 0
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
1023 0