摘要: Java内存转储是JVM运行时内存的快照,可用于分析是否存在内存浪费,并确保发生OOM时正确的内存管理。那么转储文件的内容会是什么样子呢?
JVM dump
Java 内存转储是JVM 运行时内存的快照,可让您分析是否存在内存浪费并确保发生OOM 时进行正确的内存管理。那么转储文件是什么样子的呢?
获取JVM dump文件
Dump文件获取方式分为主动式和被动式
i. 主动方法: 1. 使用jmap。这也是最常用的方法:jmap -dump:[live],format=b,file=2。使用jcmd,jcmd GC.heap_dump。 3. 使用VisualVM 通过接口转储内存。操作4.通过JMX
MBeanServer Server=ManagementFactory.getPlatformMBeanServer();HotSpotDiagnosticMXBean mxBean=ManagementFactory.newPlatformMXBeanProxy(server, ‘com.sun.management:type=HotSpotDiagnostic’, HotSpotDiagnosticMXBean.class);mxBean.dumpHeap(filePath, live);Reference(https://www.ba el ) 粪便.com/Java.
ii. 被动模式:被动模式是正常的OOM事件,通过设置参数-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=。
dump文件分析
结构图
详细结构解释
转储文件是堆内存的映射,由文件头和一组内容块组成。
文件头
它由四部分组成:麝香、版本、标识符大小和时间。
1. Musk:4字节,内容为‘J’、‘A’、‘V’,‘A’为JAVA
2.版本:字节数,具有三个值:
‘PROFILE 1.0\0’、’PROFILE 1.0.1\0’、’PROFILE 1.0.2\0’ 3.identifierSize:4 字节数字。取值为4或8,表示引用占用的字节数。
4. Time:8字节,转储文件生成时间
说明:Java类中有两种类型的成员变量。
每次创建对象时都必须为基本类型(八种基本类型)分配初始值。这是表示对象的引用类型。我在一个类中有一个引用,该引用只有一个值,它占用的空间是identifierSize,引用的对象在堆中的其他地方。例如,定义类public class Person { private intage;//4 个字节。 private String name;//identifierSize bytes private doubleweight ;//8 bytes} 对于new Person(),空间大小为对象头大小+4+identifierSize+8字节。
测量对象大小:jdk提供了一个名为Instrumentation的工具来测试对象占用的内存大小。但是Instrumentation不能直接引用,所以必须通过Premain类(javac Premain.java)来引用。
//Premain.javapublic class Premain { public static java.lang.instrument.Instrumentation inst; public static void premain(String args, java.lang.instrument.Instrumentation inst) { Premain.inst=inst;
Manifest.mfManifest-Version: 1.0Premain-Class: PremainCan-Redefine-Classes: trueCan-Retransform-Classes: truePackaging
jar -cmf manifest.mf premain.jar Premain.class 定义执行类javac PersonTest.java。
//PersonTest.javapublic class PersonTest { public static void main(String[] args) throws Exception { Class clazz=Class.forName(‘Premain’); if (clazz !=null) { Person p=new Person(); } lang.instrument.Instrumentation inst=(java.lang.instrument.Instrumentation)clazz.getDeclaredField(‘inst’).get(null); System.out.println(‘人尺寸:[‘ + inst.getObjectSize(p) + ‘ ]B’); System.out.println(‘class size:[‘ + inst.getObjectSize(p.getClass()) + } }} 在代理中执行
java -javaagent:premain.jar 人测试结果:
person size:[32]Bclass size:[504]B
内容块
每个块由块头和块体组成
大头
块头由1字节块类型、4字节时间和4字节长度(表示该内容块占用多少字节)组成。五种常见类型是类型、字符串、类和堆栈帧。堆栈、转储块。
该字符串由一个identifierSize字节的字符串ID,后面是(length-identifierSize)字节的字符串内容(字符串中的ID稍后直接引用),后面是一个4字节的类序列(在堆栈上使用)。帧),类ID 标识符大小字节(解析类时使用),序列ID 4 字节(尚未使用),类名ID 堆栈帧标识符大小字节,帧ID 标识符大小字节,标识符大小字节方法名id,标识符大小字节方法标识ID, identifierSize bytes 类文件名ID,4 字节类序列,4 字节行号堆栈,4 字节堆栈序列号,4 4 字节行程序号,4 字节帧号,后面跟着帧的iddump 块,后面跟随的identifierSize 字节是以下内容所有对象。每个对象由1 字节子类型和对象内容组成。 gc有六种子类型:根GC、线程对象GC、类GC、对象GC、基本类型数组GC、对象数组GC。根
gc root有4种结构和8种类型
identifierSize 字节对象ID,类型为SYSTEM_CLASS、BUSY_MONITOR、UNKNOWNidentifierSize 字节对象ID,4 字节线程序列号,类型为NATIVE_STACK、THREAD_BLOCKidentifierSize 字节对象ID,4 字节线程序列号,栈帧深度为4 字节,类型为JAVA_LOCAL、NATIVE_LOCALidentifierSize bytes 对象ID of,identifierSize字节的全局refId(尚未使用),类型为NATIVE_STATICgc根图
GC root 是垃圾收集追溯的源头。每个GC根必须回收无法跟踪的对象。
只有系统类、classLoader 为null 的类才是gc root。每个类都是一个gc根线程栈。每个对象都是一个gc 根系统保留对象。 GC路线
类对象
1、基本信息:
identifierSize字节类对象ID,4字节堆栈序列号,identifierSize字节父类对象ID,identifierSize字节classLoader对象ID,identifierSize字节签名者对象ID,identifierSize字节保护域对象ID,identifierSize字节字节为id1和id2,4字节保存的班级。实例对象的大小,一个2字节的常量,后面跟着每个常量,一个2字节的下标,一个1字节的常量类型,以及由类型决定的内容字节数(boolean/byte为1字节, char/short 为2 字节,float/int 为4 字节,double/long 为8 字节,引用类型为identifierSize 字节) 2 字节静态变量数量,后面跟每个静态变量,变量名ID ofidentifierSize 字节,1 变量类型以字节为单位,内容以几个字节为单位,内容取决于类型(参见《类对象基本信息栏》第9章) 2个字节中的成员变量个数,后面是每个成员变量的变量名ID,接下来是标识符大小。 2、说明:(1)类中的常量很多地方不被使用,所以常量的个数通常为0。 (2) 静态名称、类型和值;类的变量放在类对象中,成员变量的名称和类型也放在类对象中,而实例的值放在对象内的实例中。
实例对象
一、基本信息:
identifierSize Bytes 实例对象ID 4 字节堆栈序列号identifierSize Bytes 类ID 4 字节占用字节实例变量2 的值。
实例的值是实例对象的成员变量值,顺序是类对象基本信息第11条的顺序,然后是类对象的变量值。父类中变量的基类型具有默认值。引用类型默认值为0,占用字节数(参见类对象基本信息第9条)
基本类型数组
1、基本信息:
identifierSize 字节数组对象ID 4 字节堆栈序列号4 字节数组长度1 字节元素类型元素值列表2. 说明:
元素值(类对象基本信息见第9条) 对象数组
一、基本信息:
IdentifierSize 的数组对象ID Bytes 4 个字节的堆栈序列号IdentifierSize Bytes 数组的长度元素值列表类id 元素
内存分配
当线程启动时,进程移动到系统内存以生成线程堆栈,并在方法调用完成时终止堆栈帧。 running process (if) 当对对象执行新操作时,进程会移动到堆区域以申请其部分内存。 有关运行时内存的更多信息,请参阅相关信息。
内存回收规则
如果对象无法通过gc 并且根引用可达,则该对象可能会被回收。
仅当实例被回收时,实例属性才能被回收(仅限强引用)。只有当该类的所有实例都已被回收时,该类才能被回收。仅当类被回收时,类对象的父类、classLoader 对象、signer 对象和保护域对象才用大括号括起来。 public void test(){Object a=new Object();//obj 1Object b=new Object();//obj 2{Object c=new Object();//obj 3a=null;//obj 1 可以berecycle}//obj 3 can berecycle }//obj 2 can berecycle
分析工具简介
可以使用jdk提供的jhat工具来分析dump文件。
jhat xxx.dump jhat 加载并解析xxx.dump 文件并打开一个简单的Web 服务。默认端口为7000。您可以通过浏览器查看内存中的一些统计信息。
常见用法
1. 在浏览器中打开http:/127.0.0.1:7000。
列出了几个函数,包括包中每个类的概述以及每个函数的导航。
2. 单击页面的“堆内存统计”。
有一个表格显示了对象的类型、实例的数量以及实例占用的内存量,因此您可以一目了然地看到哪些类型的对象占用了最多的内存。
3、点击看起来消耗内存过多的类名,可以查看该类的详细信息。
主要是显示该类每个实例的大小以及一些链接导航。
4. 单击“按类型列出的参考资料摘要”。
如果某个特定类型的对象过多,则引用该类型的类的对象可能过多。
本质上,您可以通过将一些简单的页面查询与原始代码相结合来初步识别内存泄漏。
综上所述,转储文件的结构相对简单,对于分析线程执行情况非常有用。这也是每个Java程序员必须掌握的高级技能之一。
作者:华为云开发者社区
链接:https://segmentfault.com/a/1190000023144346
资料来源:SegmentFault Sifou
原创文章,作者:小条,如若转载,请注明出处:https://www.sudun.com/ask/82411.html