# 11. 内存对齐
# 11.1 问题引出
type S1 struct {
num1 int16
num2 int32
}
type S2 struct {
num1 int16
num2 int32
}
func main() {
fmt.Println(unsafe.Sizeof(S1{})) // 8
fmt.Println(unsafe.Sizeof(S2{})) // 8
}
为什么 S1{}
和 S2{}
的大小都为 8bytes
? —— 内存对齐
# 11.2 内存对齐
- 非内存对齐:内存的原子性和效率受影响
- 内存对齐:提高内存操作效率,有利于内存原子性
# 11.3 对齐系数
- 对齐系数:变量的内存地址必须被对齐系数整除。
unsafe.Alignof()
: 可以查看值在内存中的对齐系数。
# 11.4 基本类型对齐
fmt.Printf("bool size: %d, align: %d\n", unsafe.Sizeof(bool(true)), unsafe.Alignof(bool(true)))
fmt.Printf("byte size: %d, align: %d\n", unsafe.Sizeof(byte(0)), unsafe.Alignof(byte(0)))
fmt.Printf("int8 size: %d, align: %d\n", unsafe.Sizeof(int8(0)), unsafe.Alignof(int8(0)))
fmt.Printf("int16 size: %d, align: %d\n", unsafe.Sizeof(int16(0)), unsafe.Alignof(int16(0)))
fmt.Printf("int32 size: %d, align: %d\n", unsafe.Sizeof(int32(0)), unsafe.Alignof(int32(0)))
fmt.Printf("int64 size: %d, align: %d\n", unsafe.Sizeof(int64(0)), unsafe.Alignof(int64(0)))
输出:
bool size: 1, align: 1
byte size: 1, align: 1
int8 size: 1, align: 1
int16 size: 2, align: 2
int32 size: 4, align: 4
int64 size: 8, align: 8
结论:基本类型的对齐系数跟它的长度一致。
# 11.5 结构体对齐
- 结构体对齐分为内部对齐和结构体之间对齐
# 11.5.1 结构体内部对齐
- 指的是结构体内部成员的相对位置(偏移量);
- 每个成员的偏移量是 自身大小 和 对齐系数 的较小值的倍数
type Demo struct {
a bool //size=1,align=1
b string //size=16,align=8
c int16 //size=2,align=2
}
- a 的偏移量必须是 1 的倍数;(1 和 1 的较小值是 1)
- b 的偏移量必须是 8 的倍数;(16 和 8 的较小值是 8)
- c 的偏移量必须是 2 的倍数;(2 和 2 的较小值是 2)
# 11.5.2 结构体长度填充
- 结构体通过填充长度,来对齐系统字长;
- 结构体长度是 最大成员长度 和 系统字长 较小值的整数倍;
通过调整 struct 成员变量顺序优化内存使用
- 如将 Demo 中的 b 和 c 调换位置,那么就可以省略一个系统字长了。
# 11.5.3 结构体之间对齐
- 结构体之间对齐,是为了确定结构体的第一个成员变量的内存地址,以让后面的成员地址都合法;
- 结构体的对齐系数是 其成员的最大对齐系数;
# 11.5.4 空结构体对齐
- 空结构体单独存在时,其内存地址为
zerobase
; - 空结构体出现在结构体中时,地址跟随前一个变量;
- 空结构体出现在结构体最后,如果开启了一个新的系统字长,则需要补零,防止与其他结构体混用地址;