注意,一下讨论的状况都是基于32位机,不适用用64位机,JVM是sun的HotSpot,不一样的虚拟机实现可能会不一样 java
规则一:每一个对象被按照8bytes粒度对齐(数组除外) c++
在jvm中每一个对象(数组除外)都有一个头,这个头有两个字,第一个字存储的时对象的一些标志位信息,例如:锁标志位、经历了几回gc等信息,第二个字是一个引用,指向了这个类的类信息。这里jvm给这两个字留了8个字节的空间(这个为啥用8个字节空间不是很清楚,一个字即两个字节,我一直认为4个字节就够了) 数组
按规则一:new Object();这个Object实例就占用了8个字节 jvm
规则二:为类属性分配存储空间时不是按照类中定义的属性顺序,而是按以下的顺序: 工具
一、double\long;----8bytes spa
二、int\float;----4bytes orm
三、char\short;----2bytes 对象
四、boolean\byte;----1bytes 继承
五、reference;----4bytes 内存
例如:
Java代码
public class A {
byte a;
char b;
int c;
long d;
Object e;
}
属性 须要字节数 累积字节数
header 8bytes 8
long:d 8bytes 16
int:c 4bytes 20
char:b 2bytes 22
byte:a 1bytes 23
Object:e 4bytes 27
padding 5bytes 32
最后一行padding 5bytes的目的是,规则一中描述每一个对象按照8个字节的粒度对齐,这样下一个分配的对象的开始位置必须在8的倍数上,而离27最近的8的倍数是32,所以加了5bytes。A占用32bytes
能够用jmap看一下这个计算是否准确
规则三:对于继承时,要按照规则二先计算父类的类属性占用状况,再按照规则二计算子类的类属性占用状况,不能将父类和子类的属性混合在一块儿按规则二分配。
例如:
Java代码
class B{
long a;
int b;
int c;
}
class BB extends B{
long d;
}
属性 占用字节数 累计字节数
header 8 8
a 8 16
b 4 20
c 4 24
d 8 32
这里累计字节正好是8的倍数,知足规则一,所以不用padding字节。BB对象内存占用了32bytes
规则四:父类的最后一个属性和子类第一个属性必须按4个字节的倍数对齐
例如:
Java代码
class B{
long a;
int b;
char c;
}
class BB extends B{
long d;
}
属性 占用的字节 累计字节
head 8 8
a 8 16
c 2 18
padding 2 20
d 8 28
padding 4 32
第一次padding2是由于属性c分配内存后,不知足父类最后一个属性和子类第一个属性按4字节粒度对齐(18除4除不开),所以须要添加两个字节使其能够按4字节粒度对齐。
第二次paadding4是依据规则一
规则五:当子类的第一个属性是double或long,可是父类不能按8字节粒度对齐时,子类内存分配时的顺序将不按规则二进行,而是按:先int\float、char\short、boolean\byte、reference、long\double
例如:
Java代码
class A{
byte a;
}
class B extends A{
long b;
short c;
byte d;
}
属性 占用字节数 累计占用字节数
head 8 8
a 1 9
padding 3 12
c 2 14
d 1 15
padding 1 16
b 8 24
第一次 padding 3是根据规则四
第二次 padding 1是由于b属性是8个字节,所以须要按8个字节粒度对齐。B占用24bytes
对于数组,与普通对象不一样的是在头部,头部多了4个字节用于存储长度信息。所以数组的head是12bytes而不是8bytes