一、为何需要内存对齐?

1.平台(移植性)

不是所有的硬件平台都能够访问任意地址上的任意数据。例如:特定的硬件平台只允许在特定地址获取特定类型的数据,否则会导致异常情况

2.性能

若访问未对齐的内存,将会导致 CPU 进行两次内存访问,并且要花费额外的时钟周期来处理对齐及运算。而本身就对齐的内存仅需要一次访问就可以完成读取动作,这显然高效很多,是标准的空间换时间做法

二、内存对齐的基本原则

1.结构体的成员变量,第一个成员变量的偏移量为 0。往后的每个成员变量的对齐值必须为编译器默认对齐长度(#pragma pack(n))或 当前成员变量类型的长度 (unsafe. Sizeof ), 取最小值作为当前类型的对齐值 。其偏移量必须为对齐值的整数倍

2.结构体本身,对齐值必须为编译器默认对齐长度(#pragma pack(n))或 结构体的所有成员变量类型中的最大长度 ,取最大值的最小整数倍作为对齐值

3.结合以上两点,可得知若编译器默认对齐长度(#pragma pack(n))超过结构体内成员变量的类型最大长度时,默认对齐长度是没有任何意义的

在不同平台上的编译器都有自己默认的 “对齐系数”,可通过预编译命令 #pragma pack(n) 进行变更,n 就是代指 “对齐系数”。一般来讲,常用的平台的系数如下:

32 位:4

64 位:8

三、如何利用内存对齐来优化程序?

1.根据计算对齐值进行结构体成员之间顺序的调整,在一定程度上可以减少结构体占用的总内存

2.通过分析偏移量和对齐值,准确计算每个成员在结构体中的 偏移地址 (这里可以借助unsafe.Alignof和unsafe.Offsetof方法

四、 示例

type Test1 struct {

a bool

b int32

c int8

d int64

e byte

}

type Test2 struct {

e byte

c int8

a bool

b int32

d int64

}

func main() {

t1 := Test1{}

t2 := Test2{}

fmt. Printf (“t1 size: %d, align: %d\n”, unsafe.Sizeof(t1), unsafe.Alignof( t1))

fmt.Printf(“t2 size: %d, align: %d\n”, unsafe.Sizeof(t2), unsafe.Alignof(t2))

}

输出结果:

t1 size: 32, align: 8

t2 size: 16, align: 8

通过测试结果可以发现,调整了结构体成员变量的字段顺序,就减少了结构体的内存占用大小