Golang结构体内存布局

✅ 一句话总结:

Go 的结构体不是简单相加大小,而是根据对齐规则填充 padding(填充字节),最终大小必须是最大对齐值的倍数。

为什么要关心对齐

  • 你正在编写的代码在性能(CPU、Memory)方面有一定的要求
  • 你正在处理向量方面的指令
  • 某些硬件平台(ARM)体系不支持未对齐的内存访问

为什么要做对齐

  • 平台(移植性)原因:不是所有的硬件平台都能够访问任意地址上的任意数据。例如:特定的硬件平台只允许在特定地址获取特定类型的数据,否则会导致异常情况
  • 性能原因:若访问未对齐的内存,将会导致 CPU 进行两次内存访问,并且要花费额外的时钟周期来处理对齐及运算。而本身就对齐的内存仅需要一次访问就可以完成读取动作

在上图中,假设从 Index 1 开始读取,将会出现很崩溃的问题。因为它的内存访问边界是不对齐的。因此 CPU 会做一些额外的处理工作。如下:

  1. CPU 首次读取未对齐地址的第一个内存块,读取 0-3 字节。并移除不需要的字节 0
  2. CPU 再次读取未对齐地址的第二个内存块,读取 4-7 字节。并移除不需要的字节 5、6、7 字节
  3. 合并 1-4 字节的数据
  4. 合并后放入寄存器

从上述流程可得出,不做 "内存对齐" 是一件有点 "麻烦" 的事。因为它会增加许多耗费时间的动作

而假设做了内存对齐,从 Index 0 开始读取 4 个字节,只需要读取一次,也不需要额外的运算。这显然高效很多,是标准的空间换时间做法

一、先看结构体定义

复制代码
type Test struct {
    a bool   // 1
    b int32  // 4
    c int8   // 1
    d int64  // 8
    e byte   // 1
}

单独大小确实是:

1 + 4 + 1 + 8 + 1 = 15 字节

但 Go 不允许你这样省空间,因为「对齐」。

二、对齐规则最容易误解,看我简化后的版本

对齐规则(简化版)

① 每个字段必须放在 它对齐值的整数倍偏移地址上

|-------|----|-------|
| 类型 | 大小 | 对齐值 |
| bool | 1 | 1 |
| int8 | 1 | 1 |
| byte | 1 | 1 |
| int32 | 4 | 4 |
| int64 | 8 | 8 |

② 结构体整体大小必须是 最大对齐值(这里是 8) 的整数倍

三、按顺序计算内存布局(会发现 padding 出现了)

⭐ 步骤 1:字段 a(bool)

|----|-----|------|-------------|-------|----|
| 字段 | 对齐值 | 当前偏移 | 可放? | 放到偏移 | 占用 |
| a | 1 | 0 | 0 % 1 = 0 ✔ | 0 | 1B |

⭐ 步骤 2:字段 b(int32)

b 对齐值是 4

当前 offset=1

1 不是 4 的倍数,必须填 padding,填到 4 位置。

所以需要补:4 - (1 % 4) = 3 字节

|--------|----------------|
| 填充 | 新偏移 |
| +3 | offset = 4 |

然后放 b,占 4 字节:

offset = 4 + 4 = 8

⭐ 步骤 3:字段 c(int8)

int8 对齐值 = 1 → 没问题

c 放在 offset 8,占 1 字节

offset = 9

⭐ 步骤 4:字段 d(int64)

对齐值 8

当前 offset=9

但 9 % 8 = 1 ≠ 0

需要补齐到下一个 8 对齐地址:16

16 - 9 = 7 字节 padding

offset = 16

放 d,占 8 字节:

offset = 16 + 8 = 24

⭐ 步骤 5:字段 e(byte)

byte 对齐值=1,无需对齐

放在 offset=24,占 1 字节

offset = 25

⭐ 步骤 6:结构体整体对齐

结构体的对齐值 = 最大字段对齐值 = 8

但当前结构体大小=25

必须让 25 补齐到 8 的倍数:

下一个 8 的倍数是 32

所以需要额外 padding:

32 - 25 = 7 字节

最终结构体大小 = 32 字节

🎉 四、你看到的最终 layout(图示)

复制代码
a xxx | bbbb | c xxx | xxx | dddd dddd | e xxx | xxx
0     4      8       16      24        25     32(end)

更清晰一点(每段代表 4 字节):

复制代码
[ a . . . ] [ b b b b ] [ c . . . ] [ . . . . ]
[ d d d d ] [ d d d d ] [ e . . . ] [ . . . . ]

. 表示 padding(填充字节)

五、实验输出结果

复制代码
Test size: 32, align: 8

六、为什么 Go 有这种对齐机制?

为了 CPU 高效访问内存。

CPU 访问 misaligned memory(未对齐地址)速度会变慢,甚至在某些平台会直接报错。

所以 Go 编译器自动对齐字段,让结构体在内存中更高效。

如果想让结构体更节省空间怎么办?

👉 调整字段顺序(从大到小)

复制代码
type Test2 struct {
    d int64
    b int32
    a bool
    c int8
    e byte
}
相关推荐
世界尽头与你40 分钟前
Go pprof 调试信息泄露漏洞
安全·网络安全·golang·渗透测试
6***v41742 分钟前
搭建Golang gRPC环境:protoc、protoc-gen-go 和 protoc-gen-go-grpc 工具安装教程
开发语言·后端·golang
水痕0143 分钟前
go使用cobra来启动项目
开发语言·后端·golang
豆浆whisky4 小时前
Go并发模式选择指南:找到最适合你项目的并发方案|Go语言进阶(19)
开发语言·后端·golang
百***480714 小时前
【Golang】slice切片
开发语言·算法·golang
q***925114 小时前
Windows上安装Go并配置环境变量(图文步骤)
开发语言·windows·golang
稚辉君.MCA_P8_Java1 天前
通义 Go 语言实现的插入排序(Insertion Sort)
数据结构·后端·算法·架构·golang
源代码•宸1 天前
GoLang写一个简单版生命游戏模拟器
经验分享·笔记·学习·游戏·golang
q***01771 天前
Linux 下安装 Golang环境
linux·运维·golang