Java 对象占用内存大小
Java 对象
如果想要了解java对象在内存中的大小,必须先要了解java对象的结构。
HotSpot虚拟机中,对象在内存中存储的布局可以分为三块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)
java 对象头
- Mark Word HotSpot虚拟机的对象头(Object Header)包括两部分信息,第一部分用于存储对象自身的运行时数据, 如哈希值(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等,这部分数据的长度在32位和64位的虚拟机(暂 不考虑开启压缩指针的场景)中分别为32个和64个Bits,官方称它为“Mark Word”。
- Class Metadata Address 存储该对象的 Class 对象的地址。就是该对象属于那个Class。
- ArrayList 存储数组的长度。 如果是数组对象才会有此数据。非数组对象没有此数据。
具体对象头占用的大小如下:
长度 |
内容 |
说明 |
---|---|---|
32/64 bit |
Mark Word |
存储对象的 hashCode 或锁信息等 |
32/64 bit |
Class Metadata Address |
存储到对象类型数据的指针 |
32/64 bit |
ArrayList |
数组的长度(如果当前对象是数组) |
从上面表格中,我们可以推断出:
32位系统:
- 对象头占用:32+32=64bit。 64bit/8=8byte。
- 数组对象头占用:32+32+32=96bit。 96bit/8=12byte。
64位系统:
对象头占用:64+64=128bit。128bit/8=16byte。 数组对象头占用:64+64+64=192bit。 192bit/8=24byte。
实例数据
实例数据就是,对象中的实例变量。 实例变量类型分为:基本类型和引用类型。
类型 |
32位系统占用空间 |
64位系统占用空间 |
---|---|---|
boolean |
1 byte |
1 byte |
byte |
1 byte |
1 byte |
char |
2 byte |
2 byte |
short |
2 byte |
2 byte |
int |
4 byte |
4 byte |
float |
4 byte |
4 byte |
long |
8 byte |
8 byte |
double |
8 byte |
8 byte |
ref |
4 byte |
8 byte |
对齐填充
对象在堆中分配的最新存储单位是8byte。如果存储的数据不够8byte的倍数,则对齐填充够8的倍数个字节。
Java 对象大小分析
下面我们以 64 位的 JDK 进行分析 Java 对象在堆中的占用空间大小
代码示例一
public class StrObj1 {
private String a;
}
public class StrObj2 {
private String a;
private String b;
}
public class StrObj3 {
private String a;
private String b;
private String c;
}
public class StrObj4 {
private String a;
private String b;
private String c;
private String d;
}
public class NumObj {
private int a;
private int b;
private int c;
private int d;
}
public class Obj {
public static void main(String[] args) {
Obj obj = new Obj();
StrObj1 s1 = new StrObj1();
StrObj2 s2 = new StrObj2();
StrObj3 s3 = new StrObj3();
StrObj4 s4 = new StrObj4();
Obj[] arrObj = new Obj[10];
NumObj num = new NumObj();
//System.gc() 会出发 FullGC。
System.gc();
}
}
运行程序
java -XX:+HeapDumpBeforeFullGC -XX:HeapDumpPath=D:hproftest2.hprof -XX:-UseCompressedOops cn.com.infcn.jmat.ObjectAnalyze
启动参数说明:
-XX:+UseCompressedOops 开启指针压缩。(默认开启,该参数对64位虚拟机有用) -XX:-UseCompressedOops 关闭指针压缩。 其它参数具体 JVM 参数解释详见:生成 Heap Dump 的几种方式
因为 System.gc(); 会出发FullGC,配合-XX:+HeapDumpBeforeFullGC 参数,会在 FullGC 前会在生成一个堆dump文件:D:hproftest2.hprof
分析dump
本案例,使用 jmat 工具进行分析 dump 文件。
cn.com.infcn.jmat.Obj 对象分析
从图中我们发现 cn.com.infcn.jmat.Obj 对象占用 16 byte 空间。 非数组64位的对象头 占用16字节,而且改对象没有属性,16字节正好也是8的倍数,不需要填充,所以占用堆空间久违16字节。
cn.com.infcn.jmat.StrObj1
图中可以看出 cn.com.infcn.jmat.StrObj1 对象占用 24 byte 空间。
对象头 16 byte 1 个引用类型实例变量 16 + 8 = 24 byte
cn.com.infcn.jmat.StrObj2
图中可以看出 cn.com.infcn.jmat.StrObj1 对象占用 32 byte 空间
对象头 16 byte 2 个引用类型实例变量 16 + 2 * 8 = 32 byte
cn.com.infcn.jmat.StrObj3
图中可以看出 cn.com.infcn.jmat.StrObj1 对象占用 40 byte 空间
对象头 16 byte 3 个引用类型实例变量 16 + 3 * 8 = 40 byte
cn.com.infcn.jmat.StrObj4
图中可以看出 cn.com.infcn.jmat.StrObj1 对象占用 48 byte 空间
对象头 16 byte 4个引用类型实例变量 16 + 4 * 8 = 48 byte
cn.com.infcn.jmat.NumObj
图中可以看出 cn.com.infcn.jmat.NumObj 对象占用 32 byte 空间
4个 int 类型实例变量 16 + 4 * 4 = 32 byte
cn.com.infcn.jmat.Obj[] 数组
图中可以看出 cn.com.infcn.jmat.Obj[] 对象占用 104 byte 空间
数组对象头 24 byte 10 个 Obj 的引用。 24 + 8 * 10 = 104 byte
对象数组中存储的是对象的引用,而不是实际的数据。
代码示例 二
public class BooleanObj1 {
boolean a;
}
......
public class BooleanObj8 {
boolean a;
boolean b;
boolean c;
boolean d;
boolean e;
boolean f;
boolean g;
boolean h;
}
public class BooleanObj9 {
boolean a;
boolean b;
boolean c;
boolean d;
boolean e;
boolean f;
boolean g;
boolean h;
boolean i;
}
以指针非压缩方式执行,然后分析dump。
从图中我们发现 BooleanObj1 和 BooleanObj8 大小一样都是24。 而 BooleanObj9 的大小为32。
BooleanObj1
对象头 16 byte 1 个 boolean 实例变量。
16 + 1 = 17 byte。 因为 17 byte 不是 8 的倍数,需要 对齐填充。 所以BooleanObj1 所占空间为 24 byte。
BooleanObj8
对象头 16 byte 8 个 boolean 实例变量。
16 + 8 = 24 byte。
BooleanObj9
对象头 16 byte 9 个 boolean 实例变量
16 + 9 =25 byte 对齐填充 后为 32 byte
- Openshift高阶探索实验
- 卡卡卡!小萝莉告诉你开发iOS应用如何避免卡顿
- Bugly即将支持的ANR,精神哥告诉你是个什么鬼?
- Go语言Goroutine与Channel内存模型
- 手把手教 | 深度学习库PyTorch(附代码)
- 如何定位Obj-C野指针随机Crash(一):先提高野指针Crash率
- 如何定位Obj-C野指针随机Crash(二):让非必现Crash变成必现
- 六种开发环境部署大全:基于Openshift
- 手把手教你由TensorFlow上手PyTorch(附代码)
- 如何定位Obj-C野指针随机Crash(三):如何让Crash自报家门
- 【MIG专项测试组】腾讯手机管家实战分析:内存突增是为神马?
- 全系统栈崩溃是什么鬼?手机管家高级工程师 jaylin,教你如何抓鬼!
- 【团队分享】苍翼之刃:论File Descriptor泄漏如何导致Crash?
- 【沙龙干货分享】你要知道的N个Android适配问题
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释