【Go】内存对齐

点击阅读更多查看文章内容

什么是内存对齐

在 Go 语言中,内存对齐(Memory Alignment)是指数据在内存中的存储位置需要满足特定的对齐要求。内存对齐的目的是提高 CPU 访问内存的效率,避免因为数据未对齐而导致的性能损失。以下是关于 Go 内存对齐机制的详细介绍:

内存对齐是指数据在内存中的起始地址必须是某个值的整数倍。这个值通常是数据类型的 对齐边界(Alignment Boundary),由 CPU 架构和数据类型的大小决定。

  • 对齐边界:
    • 例如,在 64 位系统上,int64 的对齐边界通常是 8 字节。
  • 未对齐的后果:
    • 如果数据未对齐,CPU 可能需要多次访问内存才能读取或写入数据,导致性能下降。
      • CPU 以 4 字节为单位访问内存。内存地址从 0 开始,每 4 字节为一个块(例如 0-3、4-7、8-11 等)。
      • 我们需要读取一个 4 字节的整数,但它没有对齐,起始地址为 2,那么需要两次读取 0-3 4-7,才能拼接出2-5
      • 如果内存对齐,那么4字节的整数,起始地址为4,只需1次读取4-7即可
    • 在某些硬件架构上,未对齐的访问甚至会导致程序崩溃。

内存对齐规则

基本数据类型的对齐边界

数据类型 大小(字节) 对齐边界(字节)
bool 1 1
int8, byte 1 1
int16 2 2
int32, rune 4 4
int64 8 8
float32 4 4
float64 8 8
complex64 8 4
complex128 16 8
pointer 8 (64位系统) 8
string 16 (64位系统) 8
slice 24 (64位系统) 8
map 8 (64位系统) 8
channel 8 (64位系统) 8

结构体的对齐规则

  • 字段的对齐边界:
    • 每个字段的起始地址必须是其对齐边界的整数倍。
  • 结构体的对齐边界:
    • 结构体的对齐边界是其成员中最大对齐边界的值。
    • 例如,如果结构体包含 int32int64,则结构体的对齐边界是 8。
  • 结构体的大小:
    • 结构体的大小必须是其对齐边界的整数倍。
    • 编译器可能会在结构体中插入 填充字节(Padding)以满足对齐要求。

结构体内存对齐示例

以下是一个结构体内存对齐的示例:

1
2
3
4
5
type Example struct {
a bool // 1 字节
b int32 // 4 字节
c int64 // 8 字节
}
  • 对齐分析:

    • a 的对齐边界是 1,占用 1 字节。
    • b 的对齐边界是 4,因此编译器会在 ab 之间插入 3 字节的填充。
    • c 的对齐边界是 8,直接跟在 b 后面。
  • 内存布局:

    1
    | a (1) | padding (3) | b (4) | c (8) |
  • 结构体大小:

    • 总大小为 16 字节。

优化结构体布局

通过调整结构体成员的顺序,可以减少填充字节,从而节省内存。

优化前

1
2
3
4
5
type Unoptimized struct {
a bool // 1 字节
b int64 // 8 字节
c int32 // 4 字节
}
  • 内存布局:

    1
    | a (1) | padding (7) | b (8) | c (4) | padding (4) |
  • 结构体大小:

    • 总大小为 24 字节。

优化后

1
2
3
4
5
type Optimized struct {
b int64 // 8 字节
c int32 // 4 字节
a bool // 1 字节
}
  • 内存布局:

    1
    | b (8) | c (4) | a (1) | padding (3) |
  • 结构体大小:

    • 总大小为 16 字节。

查看内存对齐信息

可以使用 unsafe 包查看结构体的大小和对齐边界

1
2
fmt.Println("Size of Example:", unsafe.Sizeof(Example{}))       
fmt.Println("Alignment of Example:", unsafe.Alignof(Example{}))
作者

ShiHaonan

发布于

2025-03-03

更新于

2025-03-30

许可协议

评论