【Go】内存对齐
点击阅读更多查看文章内容
什么是内存对齐
在 Go 语言中,内存对齐(Memory Alignment)是指数据在内存中的存储位置需要满足特定的对齐要求。内存对齐的目的是提高 CPU 访问内存的效率,避免因为数据未对齐而导致的性能损失。以下是关于 Go 内存对齐机制的详细介绍:
内存对齐是指数据在内存中的起始地址必须是某个值的整数倍。这个值通常是数据类型的 对齐边界(Alignment Boundary),由 CPU 架构和数据类型的大小决定。
- 对齐边界:
- 例如,在 64 位系统上,
int64的对齐边界通常是 8 字节。
- 例如,在 64 位系统上,
- 未对齐的后果:
- 如果数据未对齐,CPU 可能需要多次访问内存才能读取或写入数据,导致性能下降。
- CPU 以 4 字节为单位访问内存。内存地址从 0 开始,每 4 字节为一个块(例如 0-3、4-7、8-11 等)。
- 我们需要读取一个 4 字节的整数,但它没有对齐,起始地址为 2,那么需要两次读取 0-3 4-7,才能拼接出2-5
- 如果内存对齐,那么4字节的整数,起始地址为4,只需1次读取4-7即可
- 在某些硬件架构上,未对齐的访问甚至会导致程序崩溃。
- 如果数据未对齐,CPU 可能需要多次访问内存才能读取或写入数据,导致性能下降。
内存对齐规则
基本数据类型的对齐边界
| 数据类型 | 大小(字节) | 对齐边界(字节) |
|---|---|---|
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 |
结构体的对齐规则
- 字段的对齐边界:
- 每个字段的起始地址必须是其对齐边界的整数倍。
- 结构体的对齐边界:
- 结构体的对齐边界是其成员中最大对齐边界的值。
- 例如,如果结构体包含
int32和int64,则结构体的对齐边界是 8。
- 结构体的大小:
- 结构体的大小必须是其对齐边界的整数倍。
- 编译器可能会在结构体中插入 填充字节(Padding)以满足对齐要求。
结构体内存对齐示例
以下是一个结构体内存对齐的示例:
1 | type Example struct { |
对齐分析:
a的对齐边界是 1,占用 1 字节。b的对齐边界是 4,因此编译器会在a和b之间插入 3 字节的填充。c的对齐边界是 8,直接跟在b后面。
内存布局:
1
| a (1) | padding (3) | b (4) | c (8) |
结构体大小:
- 总大小为 16 字节。
优化结构体布局
通过调整结构体成员的顺序,可以减少填充字节,从而节省内存。
优化前
1 | type Unoptimized struct { |
内存布局:
1
| a (1) | padding (7) | b (8) | c (4) | padding (4) |
结构体大小:
- 总大小为 24 字节。
优化后
1 | type Optimized struct { |
内存布局:
1
| b (8) | c (4) | a (1) | padding (3) |
结构体大小:
- 总大小为 16 字节。
查看内存对齐信息
可以使用 unsafe 包查看结构体的大小和对齐边界
1 | fmt.Println("Size of Example:", unsafe.Sizeof(Example{})) |

