# 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 内存对齐

image-20220814091401760
  • 非内存对齐:内存的原子性和效率受影响
image-20220814091258218
  • 内存对齐:提高内存操作效率,有利于内存原子性

# 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

结论:基本类型的对齐系数跟它的长度一致。

image-20220814092840100

# 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
}
image-20220814093817402
  • a 的偏移量必须是 1 的倍数;(1 和 1 的较小值是 1)
  • b 的偏移量必须是 8 的倍数;(16 和 8 的较小值是 8)
  • c 的偏移量必须是 2 的倍数;(2 和 2 的较小值是 2)

# 11.5.2 结构体长度填充

  • 结构体通过填充长度,来对齐系统字长;
  • 结构体长度是 最大成员长度系统字长 较小值的整数倍;
image-20220814094313044

通过调整 struct 成员变量顺序优化内存使用

  • 如将 Demo 中的 b 和 c 调换位置,那么就可以省略一个系统字长了。
image-20220814094451431

# 11.5.3 结构体之间对齐

  • 结构体之间对齐,是为了确定结构体的第一个成员变量的内存地址,以让后面的成员地址都合法;
  • 结构体的对齐系数是 其成员的最大对齐系数

# 11.5.4 空结构体对齐

  • 空结构体单独存在时,其内存地址为 zerobase
  • 空结构体出现在结构体中时,地址跟随前一个变量;

image-20220814094947035

  • 空结构体出现在结构体最后,如果开启了一个新的系统字长,则需要补零,防止与其他结构体混用地址;image-20220814095234222
上次更新: 8/14/2022, 9:54:10 AM