更新时间: 2023-04-24 13:48:07#在 Go 中恰到好处的内存对齐 fieldalignment重排序、指令对齐 type Part1 struct { a bool b int32 c int8 d int64 e byte } 占用内存: func main() { fmt.Printf("bool size: %d\n", unsafe.Sizeof(bool(true))) fmt.Printf("int32 size: %d\n", unsafe.Sizeof(int32(0))) fmt.Printf("int8 size: %d\n", unsafe.Sizeof(int8(0))) fmt.Printf("int64 size: %d\n", unsafe.Sizeof(int64(0))) fmt.Printf("byte size: %d\n", unsafe.Sizeof(byte(0))) fmt.Printf("string size: %d\n", unsafe.Sizeof("EDDYCJY")) } bool size: 1 int32 size: 4 int8 size: 1 int64 size: 8 byte size: 1 string size: 16 这么一算,Part1 这一个结构体的占用内存大小为 1+4+1+8+1 = 15 个字节。相信有的小伙伴是这么算的,看上去也没什么毛病 func main() { part1 := Part1{} fmt.Printf("part1 size: %d, align: %d\n", unsafe.Sizeof(part1), unsafe.Alignof(part1)) } 实际结果 part1 size: 32, align: 8 #内存对齐。 cpu 每次是8字节(64位)读取到寄存器#对齐规则 结构体的成员变量,第一个成员变量的偏移量为 0。往后的每个成员变量的对齐值必须为编译器默认对齐长度(#pragma pack(n))或当前成员变量类型的长度(unsafe.Sizeof),取最小值作为当前类型的对齐值。其偏移量必须为对齐值的整数倍 结构体本身,对齐值必须为编译器默认对齐长度(#pragma pack(n))或结构体的所有成员变量类型中的最大长度,取最大数的最小整数倍作为对齐值 结合以上两点,可得知若编译器默认对齐长度(#pragma pack(n))超过结构体内成员变量的类型最大长度时,默认对齐长度是没有任何意义的 最终结果 Part1 内存布局:axxx|bbbb|cxxx|xxxx|dddd|dddd|exxx|xxxx 改造后 type Part2 struct { e byte c int8 a bool b int32 d int64 } Part2 内存布局:ecax|bbbb|dddd|dddd #总结通过对比 Part1 和 Part2 的内存布局,你会发现两者有很大的不同。如下: Part1:axxx|bbbb|cxxx|xxxx|dddd|dddd|exxx|xxxx Part2:ecax|bbbb|dddd|dddd go编译器 没自动调整? #提供了fieldalignment 工具 go install golang.org/x/tools/go/analysis/passes/fieldalignment/cmd/fieldalignment@latest fieldalignment ./xxx/xxx fieldalignment -fix ./xxx/xxx 参考