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
}
相关推荐
nbsaas-boot9 小时前
Go vs Java 的三阶段切换路线图
java·开发语言·golang
modelmd12 小时前
Go 编程语言指南 练习题目分享
开发语言·学习·golang
福大大架构师每日一题15 小时前
2026年1月TIOBE编程语言排行榜,Go语言排名第16,Rust语言排名13。C# 当选 2025 年度编程语言。
golang·rust·c#
拔剑纵狂歌18 小时前
helm-cli安装资源时序报错问题问题
后端·docker·云原生·容器·golang·kubernetes·腾讯云
bing.shao20 小时前
AI在电商上架图片领域的应用
开发语言·人工智能·golang
源代码•宸20 小时前
Leetcode—712. 两个字符串的最小ASCII删除和【中等】
开发语言·后端·算法·leetcode·职场和发展·golang·dp
源代码•宸21 小时前
Golang语法进阶(Context)
开发语言·后端·算法·golang·context·withvalue·withcancel
源代码•宸21 小时前
Golang语法进阶(Sync、Select)
开发语言·经验分享·后端·算法·golang·select·pool
IT=>小脑虎1 天前
Go语言零基础小白学习知识点【基础版详解】
开发语言·后端·学习·golang
源代码•宸1 天前
Golang语法进阶(并发概述、Goroutine、Channel)
服务器·开发语言·后端·算法·golang·channel·goroutine