干货分享丨jvm系列:dump文件深度分析
摘要:java内存dump是jvm运行时内存的一份快照,利用它可以分析是否存在内存浪费,可以检查内存管理是否合理,当发生OOM的时候,可以找出问题的原因。那么dump文件的内容是什么样的呢?
JVM dump
java内存dump是jvm运行时内存的一份快照,利用它可以分析是否存在内存浪费,可以检查内存管理是否合理,当发生OOM的时候,可以找出问题的原因。那么dump文件的内容是什么样的呢?我们一步一步来
获取JVM dump文件
获取dump文件的方式分为主动和被动
i.主动方式:
1.利用jmap,也是最常用的方式:jmap -dump:[live],format=b,file=
2.利用jcmd,jcmd 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文件是堆内存的映射,由文件头和一系列内容块组成
文件头
由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一个类的成员变量有两种类型
- 基本类型(8种基本类型),它们占用byte数固定不变,每生成一个对象它们就需要给它们赋初始值,分配空间
- 是引用类型,表示一个对象,在类中只有一个引用,引用只是一个数值,所占用的空间大小为identifierSize,被引用对象即将在堆中的另一个地方
例如定义一个类
public class Person {
private int age;//4个byte
private String name;//identifierSize个byte
private 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:[]B
class size:[]B
内容块
每个块都是块头和块体组成
块头
块头由1个byte的块类型,4个byte的时间time,4个byte的长度表示此内容块占用byte数
type类型一般有5种,字符串,类,栈桢,栈,及dump块
- 字符串,由identifierSize个byte的字符串id,后面是(length-identifierSize)个byte的字符串内容(后续对字符串是直接引用的这里面的id)
- 类,由4个byte的类序列(在栈桢中使用),identifierSize个byte的类id(解析类的时候用到),4个byte的序列id(暂未使用),identifierSize个byte的类名id
- 栈桢,由identifierSize个byte的桢id,identifierSize个byte的方法名id,identifierSize个byte的方法标识id,identifierSize个byte的类文件名id,4个byte的类序列,4个byte的行号
- 栈,由4个byte的栈序号,4个byte的线程序号,4个byte的桢数量,后面就是若干个identifierSize个byte的桢id
- dump块就是所有对象的内容了,每个对象由1个byte的子类型,和对象内容结成,子类型有6种,gc root, 线程对象,类,对象,基本类型数组,对象数组
gc root
gc root有4种结构,8种类型
- identifierSize个byte的对象id,类型有SYSTEM_CLASS,BUSY_MONITOR, 及未UNKNOWN
- identifierSize个byte的对象id,4个byte的线程序列号,类型有NATIVE_STACK,THREAD_BLOCK
- identifierSize个byte的对象id,4个byte的线程序列号,4个byte的栈桢深度,类型有JAVA_LOCAL,NATIVE_LOCAL
- identifierSize个byte的对象id,identifierSize个byte的global refId(暂未使用),类型有NATIVE_STATIC
gc root示意图
gc root为垃圾收集追溯的源头,每个gc root都指向一个初始对象,无法追溯的对象是要被回收掉的

系统类,只有classLoader为null的类才是gc root,每个类都是一个gc root
线程栈,线程中方法参数,局部变量都是gc root,每个对象都是一个gc root
系统保留对象,每个对象都是一个gc root
类对象
1、基本信息:
- identifierSize个byte的类对象id
- 4个byte的栈序列号,
- identifierSize个byte的父类对象id,
- identifierSize个byte的classLoader对象id,
- identifierSize个byte的Signer对象id,
- identifierSize个byte的protection domain对象id,
- identifierSize个byte的保留id1和id2,
- 4个byte的类实例对象大小,
- 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)
- 2个byte的静态变量个数,后面是每个静态变量的,identifierSize个byte的变量名id, 1个byte的变量类型,和若干个byte的内容,内容根据类型来决定(见类对象基本信息的第9条)
- 2个byte的成员变量个数,后面是每个成员变量的,identifierSize个byte的变量名id,1个byte的变量类型
2、说明:
(1)类里面的常量很多地方都没有用上,所以常量个数一般为0
(2)类的静态变量的名称类型及值是放在类对象里面的,成员变量的名称和类型也是放在类对象里面的,但是实例的值是放在实例对象里面的
实例对象
1、基本信息:
- identifierSize个byte的实例对象id
- 4个byte的栈序列号
- identifierSize个byte的类id
- 4个byte的占用字节数
- 实例的变量的值
2、说明:
- 实例的值为实例对象的成员变量值,顺序为当前类的变量值,顺序为类对象基本信息中第11条中的顺序,然后是父类的变量值
- 变量的值基本类型都有默认值,引用类型默认值为0,占用字节数(见类对象基本信息的第9条)
基本类型数组
1、基本信息:
- identifierSize个byte的数组对象id
- 4个byte的栈序列号
- 4个byte的数组长度
- 1个byte的元素类型
- 元素的值列表
2、说明:
- 元素的值(见类对象基本信息的第9条)
对象数组
1、基本信息:
- identifierSize个byte的数组对象id
- 4个byte的栈序列号
- 4个byte的数组长度
- identifierSize个byte的元素类id
- 元素的值列表
内存分配

当一个线程启动的时候,进程会去系统内存生成一个线程栈
每当发生一次方法调用,就会向栈中压入一个栈桢,当方法调用完之后,栈桢会退出
在运行过程中,如果有对象的new操作的时候,进程会去堆区申请一块内存
关于运行时内存的详细情况,可以查找相关的资料
内存回收规则
如果一个对象不能骑过gc root引用可达,那么这个对象就可能要被回收
对象回收规则包括
- 实例属性被实例引用,只有当实例被回收了实例属性才能被回收(只针对强引用)
- 类对象被实例引用,只有当一个类的所有实例都被回收了,类才能被回收
- 类对象的父类,classLoader对象,signer对象, protection domain对象被类引用,只有当类被回收了,这些才能被回收
- 局部变量(线程栈中)的作用域为一个大括号
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

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

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

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

如果某种类型的对象太多,那么有可能是引用它的那个类的对象太多
基本上一些简单页面的查询,结合原代码,就可以初步定位内存泄漏的地方
综上,dump文件结构还是比较简单的,这对于分析线程的执行情况非常有用,也是每一个Java程序员必须掌握的高级技能之一,你学会了吗?
干货分享丨jvm系列:dump文件深度分析的更多相关文章
- 获取JVM的dump文件
获取JVM的dump文件的两种方式 1. JVM启动时增加两个参数: #出现 OOME 时生成堆 dump: -XX:+HeapDumpOnOutOfMemoryError #生成堆文件地址: -XX ...
- 在k8s中收集jvm异常dump文件到OSS
现状 加参数 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=logs/test.dump 可以实现在jvm发生内存错误后 会生成dump文件 方便开 ...
- Windows Phone App的dump文件实例分析- System.ExecutionEngineException
前言 在开始这篇文章之前我们先来讲讲如何从高度优化的Release版的Dump中找到正确的异常上下文地址,并手动恢复异常发生的第一现场. 1. 什么是异常上下文 简单来说,在windows体系的操作系 ...
- jvm系列(九):Java GC 分析
Java GC就是JVM记录仪,书画了JVM各个分区的表演. 什么是 Java GC Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之 ...
- jvm系列(五):Java GC 分析
Java GC就是JVM记录仪,书画了JVM各个分区的表演. 什么是 Java GC Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之 ...
- 干货分享丨玩转物联网IoTDA服务系列四-智能网关
摘要:该场景主要描述的是设备可以通过MQTT协议与物联网平台进行交互,用户可以在控制台产品详情中自定义Topic,通过应用侧接口或控制台创建数据转发规则,把设备上报的消息转发给其他华为云服务,供应用侧 ...
- 干货分享丨玩转物联网IoTDA服务系列五-智能家居煤气检测联动
摘要:该场景主要描述的是设备可以通过LWM2M协议与物联网平台进行交互,用户可以在控制台或通过应用侧接口创建设备联动规则,把设备上报的属性转发,通过物联网平台规则引擎转变成命令下发给其他指定设备. 场 ...
- 【腾讯Bugly干货分享】人人都可以做深度学习应用:入门篇
导语 2016年,继虚拟现实(VR)之后,人工智能(AI)的概念全面进入大众的视野.谷歌,微软,IBM等科技巨头纷纷重点布局,AI 貌似将成为互联网的下一个风口. 很多开发同学,对人工智能非常感兴趣, ...
- Windows Phone App的dump文件实例分析-Stack Overflow
前言 这篇文章我们一起来分析一个从Windows Phone Dev Center上下载下来的dump file.首先按照我上一篇的步骤设置好我们的Windbg,并按住Ctrl +D打开dumpfil ...
随机推荐
- kworkerds挖矿木马
昨天一朋友的公司服务器中了挖矿病毒,一起帮忙查看并做下记录. 病毒信息 名称:kworkerds 目录:/tmp/ 关键点:文件 -i 属性 i :这个i可就很厉害了.它可以让一个文件“不能被 ...
- 手把手教你用JS/Vue/React实现幸运水果机(80后情怀之作)
项目体验地址 免费视频教程 分别使用原生JS,Vue和React,手把手教你开发一个H5小游戏,快速上手Vue和React框架的使用. 项目截图 在线体验 在线体验 游戏介绍 幸运水果机是一款街机游戏 ...
- cnblogs 博客爬取 + scrapy + 持久化 + 分布式
目录 普通 scrapy 分布式爬取 cnblogs_spider.py 普通 scrapy # -*- coding: utf-8 -*- import scrapy from ..items im ...
- Springboot搭建Eureka并设置Eureka登录账号密码
Springboot搭建Eureka并设置Eureka登录账号密码 一.创建一个springboot项目 1.可以使用Spring Initializr,用浏览器打开http://start.spri ...
- Linux中tomcat的部署
红帽7如何配置tomcat 1.下载tomcat9.0和java-1.8 tomcat的下载地址: https://tomcat.apache.org/ java1.8是radhat7自带: [roo ...
- TensorFlow从0到1之TensorFlow多层感知机实现MINIST分类(22)
TensorFlow 支持自动求导,可以使用 TensorFlow 优化器来计算和使用梯度.它使用梯度自动更新用变量定义的张量.本节将使用 TensorFlow 优化器来训练网络. 前面章节中,我们定 ...
- 错误处理函数-lr_continue_on_error
在脚本的Run-time Settings中,可以设置在脚本运行过程中发生错误的处理方式.进入到Run-time Settings中,切换到Miscellaneous标签页,可以看到Error Han ...
- 一文带你了解Redis持久化完整版本
本文讲解知识点 持久化的简介 RDB AOF RDB与AOF的区别 持久化应用场景 对于持久化这个功能点,其实很简单没有那么复杂 演示环境 centos7.0 redis4.0 redis存放目录:/ ...
- 特性速览| Apache Hudi 0.5.3版本正式发布
1. 下载连接 源代码下载:Apache Hudi 0.5.3 Source Release (asc, sha512) 0.5.3版本相关jar包地址:https://repository.apac ...
- 看了Java的Class的源码,我自闭了
java源码之Class 源码的重要性不言而喻,虽然枯燥,但是也有拍案叫绝.这是我的源码系列第二弹,后续还会一直更新,欢迎交流.String源码可以看我的Java源码之String,如有不足,希望 ...