【华为云技术分享】干货分享丨jvm系列:dump文件深度分析

article/2025/8/31 16:15:24

摘要:java内存dumpjvm运行时内存的一份快照,利用它可以分析是否存在内存浪费,可以检查内存管理是否合理,当发生OOM的时候,可以找出问题的原因。那么dump文件的内容是什么样的呢?

 

JVM dump

java内存dumpjvm运行时内存的一份快照,利用它可以分析是否存在内存浪费,可以检查内存管理是否合理,当发生OOM的时候,可以找出问题的原因。那么dump文件的内容是什么样的呢?我们一步一步来

 

获取JVM dump文件

获取dump文件的方式分为主动和被动
i.主动方式:
1.利用jmap,也是最常用的方式:jmap -dump:[live],format=b,file=
2.
利用jcmdjcmd GC.heap_dump
3.
使用VisualVM,可以界面操作进行dump内存
4.通过JMX的方式

MBeanServer server = ManagementFactory.getPlatformMBeanServer();
HotSpotDiagnosticMXBean mxBean = ManagementFactory.newPlatformMXBeanProxy(server, "com.sun.management:type=HotSpotDiagnostic", HotSpotDiagnosticMXBean.class);
mxBean.dumpHeap(filePath, live);

参考(https://www.baeldung.com/java-heap-dump-capture)
ii.被动方式:
被动方式就是我们通常的OOM事件了,通过设置参数-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=

dump文件分析

结构示意图

dump_view.png

结构详解

dump文件是堆内存的映射,由文件头和一系列内容块组成

文件头

由musk, 版本,identifierSize, 时间4部分组成

  1. musk:4个byte,内容为'J', 'A', 'V', 'A'即JAVA

  2. version:若干byte,值有以下三种

" PROFILE 1.0\0",
" PROFILE 1.0.1\0",
" PROFILE 1.0.2\0"

3. identifierSize:4个byte数字,值为4或者8,表示一个引用所占用的byte数

4. time:8个byte,dump文件生成时间

说明:java一个类的成员变量有两种类型

  1. 基本类型(8种基本类型),它们占用byte数固定不变,每生成一个对象它们就需要给它们赋初始值,分配空间

  2. 是引用类型,表示一个对象,在类中只有一个引用,引用只是一个数值,所占用的空间大小为identifierSize,被引用对象即将在堆中的另一个地方
    例如定义一个类

public class Person {private int age;//4个byteprivate String name;//identifierSize个byteprivate double weight;//8个byte
}

当我们在new Person()的时候
它就需要申请一个空间,空间大小为 对象头大小+4+identifierSize+8个byte

对象大小的测量:
jdk提供一个测试对象占用内存大小的工具Instrumentation,但是Instrumentation没法直接引用到,需要通过agent来引用到
定义一个Premain类, javac Premain.java

//Premain.java
public class Premain {public static java.lang.instrument.Instrumentation inst;public static void premain(String args, java.lang.instrument.Instrumentation inst) {Premain.inst = inst;}
}

编写一个Manifest文件

manifest.mf
Manifest-Version: 1.0
Premain-Class: Premain
Can-Redefine-Classes: true
Can-Retransform-Classes: true

打包

jar -cmf manifest.mf premain.jar Premain.class

定义一个执行类, javac PersonTest.java

//PersonTest.java
public class PersonTest {public static void main(String[] args) throws Exception {Class clazz = Class.forName("Premain");if (clazz != null) {Person p = new Person();java.lang.instrument.Instrumentation inst = (java.lang.instrument.Instrumentation)clazz.getDeclaredField("inst").get(null);System.out.println("person size:[" + inst.getObjectSize(p) + "]B");System.out.println("class size:[" + inst.getObjectSize(p.getClass()) + "]B");}}
}

带agent执行

java -javaagent:premain.jar PersonTest

结果:

person size:[32]B
class size:[504]B

内容块

每个块都是块头和块体组成

块头

块头由1个byte的块类型,4个byte的时间time,4个byte的长度表示此内容块占用byte数
type类型一般有5种,字符串,类,栈桢,栈,及dump块

  1. 字符串,由identifierSize个byte的字符串id,后面是(length-identifierSize)个byte的字符串内容(后续对字符串是直接引用的这里面的id)

  2. 类,由4个byte的类序列(在栈桢中使用),identifierSize个byte的类id(解析类的时候用到),4个byte的序列id(暂未使用),identifierSize个byte的类名id

  3. 栈桢,由identifierSize个byte的桢id,identifierSize个byte的方法名id,identifierSize个byte的方法标识id,identifierSize个byte的类文件名id,4个byte的类序列,4个byte的行号

  4. 栈,由4个byte的栈序号,4个byte的线程序号,4个byte的桢数量,后面就是若干个identifierSize个byte的桢id

  5. dump块就是所有对象的内容了,每个对象由1个byte的子类型,和对象内容结成,子类型有6种,gc root, 线程对象,类,对象,基本类型数组,对象数组

gc root

gc root有4种结构,8种类型

  1. identifierSize个byte的对象id,类型有SYSTEM_CLASS,BUSY_MONITOR, 及未UNKNOWN

  2. identifierSize个byte的对象id,4个byte的线程序列号,类型有NATIVE_STACK,THREAD_BLOCK

  3. identifierSize个byte的对象id,4个byte的线程序列号,4个byte的栈桢深度,类型有JAVA_LOCAL,NATIVE_LOCAL

  4. identifierSize个byte的对象id,identifierSize个byte的global refId(暂未使用),类型有NATIVE_STATIC

    gc root示意图

    gc root为垃圾收集追溯的源头,每个gc root都指向一个初始对象,无法追溯的对象是要被回收掉的
    root.png

系统类,只有classLoader为null的类才是gc root,每个类都是一个gc root
线程栈,线程中方法参数,局部变量都是gc root,每个对象都是一个gc root
系统保留对象,每个对象都是一个gc root

类对象

1、基本信息:

  1. identifierSize个byte的类对象id

  2. 4个byte的栈序列号,

  3. identifierSize个byte的父类对象id,

  4. identifierSize个byte的classLoader对象id,

  5. identifierSize个byte的Signer对象id,

  6. identifierSize个byte的protection domain对象id,

  7. identifierSize个byte的保留id1和id2,

  8. 4个byte的类实例对象大小,

  9. 2个byte的常量个数,后面是每个常量的,2个byte的下标,1个byte的常量类型,和若干个byte的内容,内容根据类型来决定(boolean/byte为1个byte, char/short为2个byte,float/int为4个byte, double/long为8个byte,引用类型为identifierSize个byte)

  10. 2个byte的静态变量个数,后面是每个静态变量的,identifierSize个byte的变量名id, 1个byte的变量类型,和若干个byte的内容,内容根据类型来决定(见类对象基本信息的第9条)

  11. 2个byte的成员变量个数,后面是每个成员变量的,identifierSize个byte的变量名id,1个byte的变量类型

2、说明:
1、类里面的常量很多地方都没有用上,所以常量个数一般为0
2、类的静态变量的名称类型及值是放在类对象里面的,成员变量的名称和类型也是放在类对象里面的,但是实例的值是放在实例对象里面的

实例对象

1、基本信息:

  1. identifierSize个byte的实例对象id

  2. 4个byte的栈序列号

  3. identifierSize个byte的类id

  4. 4个byte的占用字节数

  5. 实例的变量的值

2、说明:

  1. 实例的值为实例对象的成员变量值,顺序为当前类的变量值,顺序为类对象基本信息中第11条中的顺序,然后是父类的变量值

  2. 变量的值基本类型都有默认值,引用类型默认值为0,占用字节数(见类对象基本信息的第9条)

基本类型数组

1、基本信息:

  1. identifierSize个byte的数组对象id

  2. 4个byte的栈序列号

  3. 4个byte的数组长度

  4. 1个byte的元素类型

  5. 元素的值列表

2、说明:

  1. 元素的值(见类对象基本信息的第9条)

对象数组

1、基本信息:

  1. identifierSize个byte的数组对象id

  2. 4个byte的栈序列号

  3. 4个byte的数组长度

  4. identifierSize个byte的元素类id

  5. 元素的值列表

内存分配

stack_heap.png

当一个线程启动的时候,进程会去系统内存生成一个线程栈
每当发生一次方法调用,就会向栈中压入一个栈桢,当方法调用完之后,栈桢会退出
在运行过程中,如果有对象的new操作的时候,进程会去堆区申请一块内存
关于运行时内存的详细情况,可以查找相关的资料

内存回收规则

如果一个对象不能骑过gc root引用可达,那么这个对象就可能要被回收
对象回收规则包括

  1. 实例属性被实例引用,只有当实例被回收了实例属性才能被回收(只针对强引用)

  2. 类对象被实例引用,只有当一个类的所有实例都被回收了,类才能被回收

  3. 类对象的父类,classLoader对象,signer对象, protection domain对象被类引用,只有当类被回收了,这些才能被回收

  4. 局部变量(线程栈中)的作用域为一个大括号

    public void test(){
    Object a = new Object();//obj 1
    Object b = new Object();//obj 2
    {
    Object c = new Object();//obj 3
    a = null;//obj 1可以被回收了
    }//obj 3可以回收了
    }//obj 2可以被回收了

分析工具简介

分析dump文件,我们可以用jdk里面提供的jhat工具,执行

jhat xxx.dump

jhat加载解析xxx.dump文件,并开启一个简易的web服务,默认端口为7000,可以通过浏览器查看内存中的一些统计信息

一般使用方法

1、浏览器打开http:/127.0.0.1:7000
jhat_view.png

会列出一些功能,包括package下面各个类的概览,及各个功能导航
2、点击页面的堆内存统计
jhat_group.png

有一个表格,对象类型,实例个数,实例所占用内存大小,哪种类型的对象占用了内存最多一目了然
3、点击其中认为内存消耗太多的类名查看类详情
jhat_class.png

主要展现该类下面各个实例的大小,以及一些链接导航
4、点击references summary by type
jhat_ref.png

如果某种类型的对象太多,那么有可能是引用它的那个类的对象太多

基本上一些简单页面的查询,结合原代码,就可以初步定位内存泄漏的地方

 

点击这里→了解更多精彩内容
 

相关推荐

Java从x86到Arm跨平台,实战一下!

9 个Java 异常处理的规则!

挑战10个最难的Java面试题(附答案)

手把手教你在鲲鹏上使用编程语言——Java、Python

Android手机打造你的Python&Java开发工具!


http://chatgpt.dhexx.cn/article/lWGrYHAo.shtml

相关文章

Java的dump文件分析及JProfiler使用

Java的dump文件分析及JProfiler使用 1 dump文件介绍 从软件开发的角度上,dump文件就是当程序产生异常时,用来记录当时的程序状态信息(例如堆栈的状态),用于程序开发定位问题。 idea配置发生OOM的时候指定路径生成dump文件 # 指定…

获取和分析Dump的几种工具简介

最近在进一步学习support技能的时候,了解到分析Dump的重要性,经过学习,做一些笔记。 一、什么是Dump文件。 Dump文件时进程的内存镜像。可以把程序的执行状态保存到Dump文件中。Dump文件分为内核模式Dump和用户模式Dump。其中内核模式Dump是…

JVM-通过MAT工具对dump文件进行分析

文章目录 MAT工具介绍下载安装使用OverviewHistogramDominator_TreeOQLThread_OverviewLeak Suspects MAT工具介绍 MAT(Memory Analyzer Tool),一个基于Eclipse的内存分析工具,是一个快速、功能丰富的JAVA heap分析工具,它可以帮助我们查找内…

错误号码2058:Plugin caching sha2_password could not be loaded

安装SQLyog之后,新建接口,输入密码,点击连接,发现出现了如下的错误: 1.错误的原因 SQLyog加密方式出现了错误,这时由于 插件缓存——sha2_密码无法进行加载所导致的。 2.解决方法 在开始菜单中输入命令…

2023/06/28

文章目录 1.export和export default的区别2.npm和cnpm的区别3.npm run dev/serve的区别4.slice、splice和split的区别 1.export和export default的区别 模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输…

02.07 SQLyog连接MySQL数据库时报2058错误(转载)

使用SQLyog连接MySQL时报了这样一个错 “错误号码2058,Plugin caching——sha2_passward could not be loaded:******** ”(乱码) 将报错信息翻译:“插件缓存——sha2_密码无法进行加载”,这是由于你的SQLyog mysql 密码加密方法变了&#…

【C++】2058:【例3.10】简单计算器(信息学奥赛)

好了,今天的内容就是有简单计算器了,内容的详细意思也就是一个最简单的计算器支持,-,*,/四种运算然后根据输入输出结果。(博主正在冲200粉丝,喜欢的赶紧关注!!&#xff0…

【MySQL2058】MySQL远程连接错误码2058

Docker安装的MySQL,突然服务器登录正常,但是使用SQLyog登录报异常: MySQL错误号码 2058上网上巴拉巴拉半天,各种说法都有,最后综合网上各种教程方案,解决了自己的问题,最后将自己的解决方案罗列…

解决SQLyog连接MySQL出现2058错误

解决SQLyog连接MySQL出现2058错误 1、确定你的mysql命令能运行,像下面这样: 2、如果出现这种情况: 解决方法一: 配置系统环境变量path的值,将安装MySQL路径下的bin路径:MySQL安装目录\bin,配置…

SQLyog连接数据库报错2058

命令符界面登录MySQL然后输入命令 ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 新密码; 修改密码的加密方式即可。

使用sqlyog连接数据库时出现错误(2058)解决方法

出现这个原因是MySQL8之前的版本中加密规则是mysql_native_password,而在MySQL8之后,加密规则 是caching_sha2_password。解决问题方法有两种; (1)第一种是升级图形界面工具版本 (2)第二种是把M…

SQLyog出现2058错误的解决方法

① WinR打开终端,输入cmd ②输入 mysql -u root -p 然后输入密码 (root) ③依次执行以下语句 ALTER USER’root’‘localhost’ IDENTIFIED BY ‘root’ PASSWORD EXPIRE NEVER; #修改加密规则 ALTER USER’root’‘localhost’ IDENTIFIED…

使用sqlyog连接mysql时出现错误号码2058的解决方法

1、使用命令行(winR --> cmd)登录mysql,需要输入密码;若出现如下界面,登录成功。 2、输入 use mysql;(分号要有) 3、输入 select user,host from user; 出现如下界面。 4、从上…

SQLyog连接MySQL8.0.24远程服务器 ,报错:2058的解决方法

写在前面: 我热爱技术,热爱分享,热爱生活, 我始终相信:技术是开源的,知识是共享的! 博客里面的内容大部分均为原创,是自己日常的学习记录和总结,便于自己在后面的时间里回…

sqlyog连接mysql错误码2058

一、⾸先保证你的MySQL数据库安装成功 cmd命令下mysql -u root -p 连接出错如上,原因是加密⽅式变了,现在是root的授权没刷新,我们重置⼀下密码。sqlyog配置新连接报错:错误号码2058,是因为mysql密码加密⽅法变了。 …

SQLyog连接mysql8.0时报错(错误号码2058)

本文将详细说明解决SQLyog连接mysql8.0时,SQLyog Ultimate显示报错信息并附带乱码:“错误号码2058,Plugin caching——sha2_passward could not be loaded:******** ”(最后一段信息是乱码)。 如下图所示: 将报错信息翻译过来就是…

SQLyog连接MySQL时出现2058错误解决方法

解决方案 第一步 WinR→cmd打开命令提示符, 输入以下命令:mysql -u root -p 1.1 提示Enter password,输入密码即可 1.2 如果提示“mysql不是内部或外部命令,也不是可运行的外部程序或批处理文件。” 如下图 解决此错误有两种…

Mysql出现问题:ERROR 2058: Plugin caching_sha2_passward could not be loaded解决方案

回城传送–》《数据库问题解决方案》 ❤️作者主页:小虚竹 ❤️作者简介:大家好,我是小虚竹。Java领域优质创作者🏆,CSDN博客专家🏆,华为云享专家🏆,掘金年度人气作者🏆,阿里云专家博主🏆,51CTO专家博主🏆 ❤️技术活,该赏 ❤️点赞 👍 收藏 ⭐再看,养成…

mysql出现2058,连接MySQL报“Error No.2058 Plugin caching_sha2_password could not be loaded”

问题重现 使用sqlyog连接linux系统下docker中的mysql8.0.11时报错plugin caching_sha2_password could not be loaded 问题原因 MySQL8.0新版默认使用caching_sha2_password作为身份验证插件,而旧版是使用mysql_native_password 而sqlyog默认是使用mysql_native_…

MySql8.0版本连接sqlyog时出现错误号码2058 和1045错误

错误号码2058乱码 在下载完MySQL8.0.31和sqlyog后,尝试用sqlyog连接数据库 出现了错误号码2058 Plugin caching _sha2_password could not be loaded:乱码问题 原因分析: 出现这个原因是Mysql之前的版本中加密规则是mysqlnative_password&#xff0c…