Java 对象
HotSpot 虚拟机中,对象在内存中存储的布局可以分为三块区域:对象头(Header),实例数据(Instance Data)和对齐填充(Padding)
java 对象头
HotSpot 虚拟机的对象头 (Object Header) 包括两部分信息,第一部分用于存储对象自身的运行时数据, 如哈希值(HashCode),GC 分代年龄,锁状态标志,线程持有的锁,偏向线程 ID,偏向时间戳等等,这部分数据的长度在 32 位和 64 位的虚拟机(暂 不考虑开启压缩指针的场景)中分别为 32 个和 64 个 Bits,官方称它为 "Mark Word".
长度 | 内容 | 说明 |
---|---|---|
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();
}
}
运行程序
java -XX:+HeapDumpBeforeFullGC -XX:HeapDumpPath=D:\hprof\test2.hprof -XX:-UseCompressedOops cn.com.infcn.jmat.ObjectAnalyze
启动参数说明:
开启指针压缩.(默认开启,该参数对 64 位虚拟机有用)
-XX:-UseCompressedOops
关闭指针压缩.
其它参数具体 JVM 参数解释详见: 生成 Heap Dump 的几种方式
运行完程序后,会在生成一个堆 dump 文件:D:\hprof\test2.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
来源: http://www.jianshu.com/p/7bd9139392ab