前言
第一次看到内存对齐时,俺是一头雾水,什么是内存对齐?为啥要这样做呢?带着这些疑问,俺查阅了相关资料,整理出本文。
…
首先,我们看一下T1结构体,猜一下会占用多少字节内存?
PS:本文如无特殊说明,均是指64位机器。
type T1 struct {
a int8
b int16
c int64
}
有些童鞋不假思索的就得出了答案:1+2+8 = 11
很遗憾,答案是错误的,正确应该是 1+1+2+4+8 = 16
为什么会这样呢?
要解释这个问题,我们要先理解CPU对内存的读取机制。
CPU 读取内存方式
CPU 总是按字长(word size)读取内存。64位CPU字长为8字节,32位CPU字长为4字节。
T1 在内存对齐下的存储

因为内存对齐的,CPU 读取a或b时,一次读取即可,c 占完整字长也是一次读取。
T1 在内存未对齐下的存储

因为CPU总是按字长读取,而c分配在两个字长里,需要读2次。
Go 中内存对齐保证
理解了CPU 读取内存方式,我们看下Go的内存保证规则。
一个合格的Go编译器必须保证:
- 对于任何类型的变量x , unsafe.Alignof(x) 的结果最小为1 。
- 对于一个结构体类型的变量x , unsafe.Alignof(x) 的结果为x 的所有字段的对齐保证unsafe.Alignof(x.f) 中的最大 值(但是最小为1 )。
- 对于一个数组类型的变量x , unsafe.Alignof(x) 的结果和此数组的元素类型的一个变量的对齐保证相等。
T1的对齐保证长度
fmt.Println("t1 alignOf:", unsafe.Alignof(T1{})) // t1 alignOf: 8
那字段的顺序会影响内存占用大小么? 我们看下T2,确实会影响,如果对内存的分配有比较高的性能要求,可以适当进行优化。不过一般情况下不用考虑。
type T2 struct {
a int8
// 在64位架构上,为了让字段b的地址为8字节对齐,
// 需在这里填充7个字节。在32位架构上,为了让
// 字段b的地址为4字节对齐,需在这里填充3个字节。
c int64
// 为了让类型T2的尺寸为T1的对齐保证的倍数,
// 在64位架构上需在这里填充6个字节,在32架构
// 上需在这里填充2个字节。
b int16
}
// 类型T2的尺寸在64位架构上为24个字节(1+7+8+2+6),
// 在32位架构上为16个字节(1+3+8+2+2)。
总结
内存对齐其实是空间换时间的典型应用。理解Go中内存分配规则,方便我们对内存进行估算。