概念
对齐跟数据在内存中的位置有关。如果一个变量的内存地址正好位于它长度的整数倍,他就被称做自然对齐。比如在32
位cpu
下,假设一个整型变量的地址为0x00000004
,那它就是自然对齐的。
为什么要字节对齐
-
CPU
每次寻址都是要消费时间的,并且CPU
访问内存时,并不是逐个字节访问,而是以字长(word size
)为单位访问,所以数据结构应该尽可能地在自然边界上对齐,如果访问未对齐的内存,处理器需要做两次内存访问,而对齐的内存访问仅需要一次访问,内存对齐后可以提升性能 - 有些
CPU
可以访问任意地址上的任意数据,而有些CPU
只能在特定地址访问数据,因此不同硬件平台具有差异性,这样的代码就不具有移植性,如果在编译时,将分配的内存进行对齐,这就具有平台可以移植性了
如何进行对齐?
- 基本数据类型:地址只要是它的长度的整数倍即可
- 数组:按照基本数据类型对齐,第一个对齐了后面的自然也就对齐了
- 联合:按其包含的长度最大的数据类型对齐
- 结构体:结构体中每个数据类型都要对齐
对齐数
对齐数
=编译器默认值
与 成员自身大小
两者的较小值
32位cpu上默认的指定对齐值是4
字节,64位cpu上默认的指定对齐值是8
字节
对齐规则
- 第一个成员一定对齐,位于结构体变量偏移量(offset)为0的地址处。
- 其他成员要对齐到对齐数的整数倍的地址处(
当前起始偏移量 % min(成员大小, 对齐数) == 0
) - 结构体 总大小必须为最大对齐数(每个成员都有一个对齐数)的整数倍。
- 如果嵌套了结构体的情况,先按规则计算出嵌套的结构体大小,最后结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
举例(golang)
// 64位平台,对齐参数是8
type User struct {
A int32 // 4
B []int32 // 24 ,切片结构体中每个成员占用8字节
C string // 16 ,字符串结构体类型占用16字节
D bool // 1
}
对齐参数是8,int32、[]int32、string、bool对齐值分别是4、8、8、1,占用内存大小分别是4、24、16、1,我们先根据第一条对齐规则分析User:
- 第一个字段类型是int32,对齐值是4,大小为4,所以放在内存布局中的第一位.
- 第二个字段类型是[]int32,对齐值是8,大小为24,按照第一条规则,偏移量应该是成员大小24与对齐值8中较小那个的整数倍,那么偏移量就是8,所以4-7位会由编译进行填充,一般为0值,也称为空洞,第9到32位为第二个字段B.
- 第三个字段类型是string,对齐值是8,大小为16,所以他的内存偏移值必须是8的倍数,因为user前两个字段就已经排到了第32位,所以offset为32正好是8的倍数,不要填充,从32位到48位是第三个字段C.
- 第四个字段类型是bool,对齐值是1,大小为1,所以他的内存偏移值必须是1的倍数,因为user前两个字段就已经排到了第48位,所以下一位的偏移量正好是48,正好是字段D的对齐值的倍数,不用填充,可以直接排列到第四个字段,也就是从48到第49位是第三个字段D
©著作权归作者所有:来自51CTO博客作者Golang梦工厂的原创作品,请联系作者获取转载授权,否则将追究法律责任
详解内存对齐
https://blog.51cto.com/u_14523732/5707302
网友评论