在《Java对象的内存布局》中给出了32位HotSpot虚拟机和64位HotSpot虚拟机下Java对象内存布局的对齐填充规则,为了便于在“基于Java对象内存布局计算Java对象大小的算法”中应用这些对齐填充规则,本文尝试推导这些对齐填充规则的等价形式。
本文结构分为两个部分:1)一条定理;2)分32位HotSpot虚拟机和64位HotSpot虚拟机分别进行推导。
备注:
以“Ak”表示“k-对齐”操作符。
一、一条定理
假如有:
1
2
3
4A+B%M=0
A+C+D%M=0
0<=B
0<=C+D
那么:
1B=C+D
二、推导
2.1、32位HotSpot虚拟机
从《Java对象的内存布局》中复制相应的对齐填充规则,如下所示:
1
2
3
4
5
6规则1:8种原生基本类型或者对象引用类型字段内存起始地址需满足“相应类型占据字节数-对齐”,比如“int”类型字段的内存起始地址需满足“4-对齐”,“reference”类型字段的内存起始地址需满足“4-对齐”
规则2:同一类中所有字段按照以下字段类型顺序排列分配内存,字段类型顺序为“long/double,int/float,char/short,boolean/byte,reference”
规则3:父类中所有字段排列于子类中所有字段之前
规则4:假定父类中存在至少一个字段,那么子类中首字段内存起始地址需满足“4-对齐”。并且如果此时子类中含有“long类型或者double类型”字段,而且子类中首字段内存起始地址只满足“4-对齐”,而不满足“8-对齐”,那么需要“两步走”(第一步,先从以“int/float,char/short,boolean/byte,对象引用类型”顺序排列的字段队列中选取一段“以队列起点为起点的子队列”,使得这段子队列对应的所有字段相应大小累加和在小于等于4的前提下,取到最大值为止;第二步,再遵循“规则2”排列剩余字段);否则,子类中字段继续遵循“规则2”
规则5:如果是“数组对象”,那么具体元素队列中第一个元素的内存起始地址需满足“4-对齐”或者“8-对齐”(如果数组元素类型为“long类型或者double类型”,那么需满足“8-对齐”;否则,需满足“4-对齐”)
规则6:如有必要需进行字节填充,以使得接下来毗邻的Java对象内存起始地址满足“8-对齐”
2.1.1、等价规则1
2.1.1.1、规则描述
在计算“一般实例对象obj”自身大小时,如果不考虑祖先类中字段,那么计算过程可描述为:
1obj自身大小=A8(MarkWord+类型指针+obj相应类所有字段相应大小累加和)
2.1.1.2、推导
根据“规则1,规则2和规则6”可知,计算区域主要分为3个部分:“MarkWord+类型指针,以A表示该区域字节大小”,“所有8种原生基本类型字段,以B表示该区域字节大小”和“所有对象引用类型字段,以C表示该区域字节大小”。分别以“AB,BC,CC”表示相应可能需要填充的区域。如图1所示。
图1
现在即需要证明D=AB+BC+CC,其中“D”表示执行上述“A8”操作时需要填充的最小字节大小。
证明步骤如下:
现有A+AB+B+BC+C+CC%8=0和A+B+C+D%8=0
由于A=4+4=8,因此,AB=0
由于对象引用类型字节大小为4字节,因此,0<=BC<4,且CC=0或者4,那么BC+CC<8
根据上述内容,可知0<=AB+BC+CC<8,而根据定义,D也满足0<=D<8,应用“一、一条定理”中的定理,AB+BC+CC=D得证
2.1.2、等价规则2
2.1.2.1、规则描述
在计算“一般实例对象obj”自身大小时,如果考虑祖先类中字段,那么计算过程可描述为:
1
2
3obj自身大小=A8(MarkWord+类型指针+PR(obj对应类的父类PObj)+obj对应类中所有字段相应大小累加和)
不满足“PR(PObj的父类PPObj)%8!=0 && PObj类中存在且只存在long类型或者double类型字段”:PR(obj对应的父类PObj)=A4(PR(PObj的父类PPObj)+PObj类中所有字段相应大小累加和)
满足“PR(PObj的父类PPObj)%8!=0 && PObj类中存在且只存在long类型或者double类型字段”:PR(obj对应的父类PObj)=A4(PR(PObj的父类PPObj)+4+PObj类中所有字段相应大小累加和)[这里的A4是多余的]
2.1.2.2、推导
根据“规则1,规则2,规则3,规则4和规则6”,可得以下三个计算模型。
1、计算模型1
对应“等式1”,计算区域主要分为:“MarkWord+类型指针,以A表示该区域字节大小”,“所有祖先类字段,以B表示该区域字节大小”,“本类中被提前置于B之后的字段集合,以C2表示该区域字节大小,根据‘规则4’,C2中不可能‘既包含8种原生基本类型,又包含对象引用类型’”,“本类中long或者double类型字段集合,以C1表示该区域字节大小”,“本类中除了C1和C2之外的原生基本类型字段集合,以C3表示该区域字节大小”和“本类中除了C1和C2之外的对象引用类型字段集合,以C4表示该区域字节大小”。分别以“AB,BC,CC,CD”表示相应可能需要填充的区域。如图2所示。
图2
现在即需要证明D=AB+BC+CC+CD,其中“D”表示执行上述“A8”操作时需要填充的最小字节大小。
证明步骤如下:
现有A+AB+B+C2+BC+C1+C3+CC+C4+CD%8=0和A+B+C2+C1+C3+C4+D%8=0
由于A=4+4=8,因此,AB=0
根据“规则4”,得0<=BC<=4
对象引用类型字节大小为4,故有0<=CC<4,CD=0或者CD=4
当BC=0时,有0<=CC+CD<8;当BC=4时,CC=0且CD=0;当0
根据上述内容,可知0<=AB+BC+CC+CD<8,而根据定义,D也满足0<=D<8,应用“一、一条定理”中的定理,AB+BC+CC+CD=D得证
2、计算模型2
对应“等式2”,计算区域主要分为:“本父类的所有祖先类字段,以PA表示该区域字节大小”,“本父类中被提前置于PA之后的字段集合,以PC2表示该区域字节大小”,“本父类中long或者double类型字段集合,以PC1表示该区域字节大小”,“本父类中除了PC1和PC2之外的原生基本类型字段集合,以PC3表示该区域字节大小”和“本父类中除了PC1和PC2之外的对象引用类型字段集合,以PC4表示该区域字节大小”。分别以“AC,CC和CD”来表示相应可能需要填充的区域。如图3所示。
图3
现在即需要证明D=AC+CC+CD,其中“D”表示执行上述“A4”操作时需要填充的最小字节大小。
证明步骤如下:
现有PA+PC2+AC+PC1+PC3+CC+PC4+CD%4=0和PA+PC2+PC1+PC3+PC4+D%4=0
根据“规则4”,得0<=AC<=4,且由于不满足“PR(PObj的父类PPObj)%8!=0 && PObj类中存在且只存在long类型或者double类型字段”,因此0<=AC<4
对象引用类型字节大小为4,因此,CD=0
可很容易推得:有0<=CC<4。当0
根据上述内容,可知0<=AC+CC+CD<4,而根据定义,D也满足0<=D<4,应用“一、一条定理”中的定理,AC+CC+CD=D得证
3、计算模型3
对应“等式3”,可根据“规则1,规则2,规则3,规则4和规则6”直接推得。
2.1.3、等价规则3
2.1.3.1、规则描述
在计算“数组对象obj”自身大小时,那么计算过程可描述为:
1obj自身大小=A8(MarkWord+类型指针+数组长度记录区域+数组长度*数组元素类型字节大小)
2.1.3.2、推导
根据“规则5和规则6”可知,计算区域主要分为两个部分:“MarkWord+类型指针+数组长度记录区域,以A表示该区域字节大小”和“数组元素队列,以B表示该区域字节大小”。分别以“AB,BB”表示相应可能需要填充的区域。如图4所示。
图4
现在即需要证明D=AB+BB,其中“D”表示执行上述“A8”操作时需要填充的最小字节大小。
证明步骤如下:
现有A+AB+B+BB%8=0和A+B+D%8=0
有A=4+4+4=12
当数组元素类型为“long”或者“double”时,AB=4,BB=0;否则,AB=0,0<=BB<8
根据上述内容,可知0<=AB+BB<8,而根据定义,D也满足0<=D<8,应用“一、一条定理”中的定理,AB+BB=D得证
2.2、64位HotSpot虚拟机
从《Java对象的内存布局》中复制相应的对齐填充规则,如下所示:
1
2
3
4
5
6规则1:8种原生基本类型或者对象引用类型字段内存起始地址需满足“相应类型占据字节数-对齐”,比如“int”类型字段的内存起始地址需满足“4-对齐”,“reference”类型字段的内存起始地址需满足“8-对齐”
规则2:同一类中所有字段按照以下字段类型顺序排列分配内存,字段类型顺序为“long/double,int/float,char/short,boolean/byte,reference”
规则3:父类中所有字段排列于子类中所有字段之前
规则4:假定父类中存在至少一个字段,那么子类中首字段内存起始地址需满足“8-对齐”
规则5:如果是“数组对象”,那么具体元素队列中第一个元素的内存起始地址需满足“8-对齐”
规则6:如有必要需进行字节填充,以使得接下来毗邻的Java对象内存起始地址满足“8-对齐”
2.2.1、等价规则1
2.2.1.1、规则描述
在计算“一般实例对象obj”自身大小时,如果不考虑祖先类中字段,那么计算过程可描述为:
1obj自身大小=A8(MarkWord+类型指针+obj相应类所有字段相应大小累加和)
2.2.1.2、推导
根据“规则1,规则2和规则6”可知,计算区域主要分为3个部分:“MarkWord+类型指针,以A表示该区域字节大小”,“所有8种原生基本类型字段,以B表示该区域字节大小”和“所有对象引用类型字段,以C表示该区域字节大小”。分别以“AB,BC,CC”表示相应可能需要填充的区域。如图5所示。
图5
现在即需要证明D=AB+BC+CC,其中“D”表示执行上述“A8”操作时需要填充的最小字节大小。
证明步骤如下:
现有A+AB+B+BC+C+CC%8=0和A+B+C+D%8=0
由于A=8+8=16,因此,AB=0
由于对象引用类型字节大小为8字节,因此,0<=BC<8,且CC=0,那么BC+CC<8
根据上述内容,可知0<=AB+BC+CC<8,而根据定义,D也满足0<=D<8,应用“一、一条定理”中的定理,AB+BC+CC=D得证
2.2.2、等价规则2
2.2.2.1、规则描述
在计算“一般实例对象obj”自身大小时,如果考虑祖先类中字段,那么计算过程可描述为:
1
2obj自身大小=A8(MarkWord+类型指针+PR(obj对应类的父类PObj)+obj对应类中所有字段相应大小累加和)
PR(obj对应的父类PObj)=A8(PR(PObj的父类PPObj)+PObj类中所有字段相应大小累加和)
2.2.2.2、推导
根据“规则1,规则2,规则3,规则4和规则6”,可得以下两个计算模型。
1、计算模型1
对应“等式1”,计算区域主要分为:“MarkWord+类型指针,以A表示该区域字节大小”,“所有祖先类字段,以B表示该区域字节大小”,“本类中8种原生基本类型字段集合,以C表示该区域字节大小”和“本类中对象引用类型字段集合,以D表示该区域字节大小”。分别以“AB,BC,CD,DD”表示相应可能需要填充的区域。如图6所示。
图6
现在即需要证明E=AB+BC+CD+DD,其中“E”表示执行上述“A8”操作时需要填充的最小字节大小。
证明步骤如下:
现有A+AB+B+BC+C+CD+D+DD%8=0和A+B+C+D+E%8=0
由于A=8+8=16,因此,AB=0
B区域一定满足“8-对齐”,因此,BC=0
对象引用类型字节大小为8,故而有0<=CD<8,DD=0
根据上述内容,可知0<=AB+BC+CD+DD<8,而根据定义,E也满足0<=E<8,应用“一、一条定理”中的定理,AB+BC+CD+DD=E得证
2、计算模型2
对应“等式2”,计算区域主要分为:“本父类的所有祖先类字段,以PA表示该区域字节大小”,“本父类中原生基本类型字段集合,以PB表示该区域字节大小”和“本父类中对象引用类型字段集合,以PC表示该区域字节大小”。分别以“AB,BC和CC”表示相应可能需要填充的区域。如图7所示。
图7
现在即需要证明D=AB+BC+CC,其中“D”表示执行上述“A8”操作时需要填充的最小字节大小。
证明步骤如下:
现有PA+AB+PB+BC+PC+CC%8=0和PA+PB+PC+D%8=0
PA区域一定满足“8-对齐”,因此,AB=0
对象引用类型字节大小为8,故而有0<=BC<8,且CC=0
根据上述内容,可知0<=AB+BC+CC<8,而根据定义,D也满足0<=D<8,应用“一、一条定理”中的定理,AB+BC+CC=D得证
2.2.3、等价规则3
2.2.3.1、规则描述
在计算“数组对象obj”自身大小时,计算过程可描述为:
1obj自身大小=A8(MarkWord+类型指针+数组长度记录区域+4+数组长度*数组元素类型字节大小)
2.2.3.2、推导
根据“规则5和规则6”,直接可推得。