虚拟机:Java HotSpot(TM) 64-Bit Server VM (25.221-b11, mixed mode)
对象的内存以字节为单位,必须是8的倍数,它的构成由3部分组成:对象头+实例数据+对齐内存。对象头主要包括对象的运行行元数据,比较哈希码、GC分代年龄、锁状态标志还有类型指针,类型指针指向类元数据,表明该对象所属类型。实例数据包括自身数据和所有父级数据,所有父级占内存大小都是8的倍数,没有就需要补齐。类型指针一般为4字节,在关闭压缩普通对象指针时(-XX:+UseCompressedOops)为8字节,UseCompressedOops默认是开启的,只有虚拟机内存达到32G以上,4个字节已经无法满足寻址需求时,才需要关闭该参数。
普通对象头除类型指针外的大小为8字节,在开启压缩总大小为12字节,不开启压缩总大小为16字节;数组对象头在开启压缩时是16字节,不开启压缩为24字节。
各种类型大小如下:
对象类型 | 字节 | |
boolean | 1 | |
byte | 1 | |
short | 2 | |
char | 2 | |
int | 4 | |
float | 4 | |
long | 8 | |
double | 8 | |
引用类型 | 开启指针压缩为4,不开启为8 | |
普通对象头 | 开启指针压缩为12,不开启为8 | |
数据对象头 | 开启指针压缩为16,不开启为24 |
例1:
public class Persion { int id; }
开启压缩普通对象指针时,对象大小:(8+4)+4+0=16字节,补齐0字节
关闭压缩普通对象指针时,对象大小:(8+8)+4+4=24字节,补齐4字节
例2:
public class Persion { int id; String name; int age; Date birthday; }
开启压缩普通对象指针时,对象大小:(8+4)+(4+4+4+4)+4=32字节,补齐了4字节。
关闭压缩普通对象指针时,对象大小:(8+8)+(4+8+4+8)+0=40字节,补齐了0字节
例3:
public class Persion { int id; String name; int age; Date birthday; boolean school; } public class Student extends Persion { int b; }
开启压缩普通对象指针时:
Person对象的大小为:(8+4)+(4+4+4+4+1)+7=32字节,补齐了7字节。
Student对象的大小为:(8+4)+(4+(4+4+4+4+1+7)) +0=40字节,Person对象补齐了7字节。
关闭压缩普通对象指针时:
Person对象的大小为:(8+8)+(4+8+4+8+1)+7=48字节,补齐7字节
Student对象的大小为:(8+8)+(4+(4+8+4+8+1+7))+4=56字节,Person对象补齐了7字节,Student补齐4字节。
例4:
class MyList { Persion[] elementData = new Persion[10]; }
开启压缩普通对象指针时:
MyList对象大小:(8+4)+4+0=16字节,补齐了0字节
new Person[10]数组对象大小:16+4*10+0=56字节,补齐了0字节
关闭压缩普通对象指针时:
MyList对象大小:(8+8)+8+0=24字节,补齐了0字节
new Person[10]数组对象大小:24+8*10+0=104字节,补齐了0字节
例5:
@SuppressWarnings("serial") class MyArrayList extends ArrayList<Persion> { }
AarryList和它父级只有3个非静态成员变量(size为整数、elementData为数组引用,在ArrayList类中,modCount为整数在AbstractList类中),静态变量内存在堆的另外部分,不在对象内。
开启压缩普通对象指针时:
MyArrayList对象大小:(8+4)+(0+(4+4+(4+4)))+4=32字节,AbstractList做了一次补齐,整体做了一次补齐。(与下图结果不符,后面抽时间在看看)
关闭压缩普通对象指针时:
MyArrayList对象大小:(8+8)+(0+(4+8+(4+4)+4))+0=40字节,AbstractList做了一次补齐,ArrayList做了一次补齐