前言之前遇到过这样一个情况(发现问题的结构体并不长这样, 不过为了引出问题, 改了一下):
type Test struct {b booli3 int32i8 int8i64 int64by byte}func main() {t := Test{}fmt.Printf("%d", unsafe.Sizeof(t))}创建一个结构体, 查看一下其内存占用. 看结果前先简单算一下:
- bool: 1B
- int32: 4B
- int8: 1B
- int64: 8B
- byte: 1B

文章插图
32个字节???这不坑我么.内存占用直接多出一倍.
探索通过查找资料, 发现了这样一个名词: 内存对齐. 什么是内存对齐呢?
简单说, 就是CPU在读取数据的时候, 并不是一个字节一个字节读取的, 而是一块一块读取的. 那么这个快是多大呢? 根据CPU位数不同而不同.
而GO编译器在编译的时候, 为了保证内存对齐, 对每一个数据类型都给出了对齐保证, 将未对齐的内存留空. 如果一个类型的对齐保证是4B, 那么其数据存放的起始地址偏移量必是4B 的整数倍. 而编译器给出的这个对齐保证是多少呢? 不同版本不同平台的编译器不尽相同, 可以通过函数unsafe.Alignof 来获取.
通过分析之前的数据结果, 就能大致理解了. 先来看一下几个类型对齐保证的值:
fmt.Printf("bool: %dn", unsafe.Alignof(bool(false))) fmt.Printf("int32: %dn", unsafe.Alignof(int32(0))) fmt.Printf("int8: %dn", unsafe.Alignof(int8(0))) fmt.Printf("int64: %dn", unsafe.Alignof(int64(0)))fmt.Printf("byte: %dn", unsafe.Alignof(byte(0)))结果如下:
文章插图
来尝试一个一个放到内存中(下图中每个空白代表一个字节):
1.放入bool: 其对齐保证为1, 第一个变量, 直接放入即可.

文章插图
2.放入int32. 其对齐保证为4, 既偏移量为4的整数倍. 而现有地址中, 首个4的整数倍为第四个字节(中间三字节留空).

文章插图
按照这个思路, 依次将后面的变量放入, 结果占用的内存为(其中字母依次为变量占用, X为对齐留空):
AXXX BBBB CXXX XXXX DDDD DDDD E
但是这才25个字节啊. 和实际的32字节还差点呢. 别急, 再看一下结构体的对齐保证, 发现是8B. 上面不是8B 的整数倍, 往后补零. 结果:
AXXX BBBB CXXX XXXX DDDD DDDD EXXX XXXX
如此一来, 就正好32位了. 结构体的对齐保证, 为其成员变量对齐保证的最大值.
why那么编译器为什么要做内存对齐这种事情呢? 举个例子, 如果不做内存对齐, 那么下面这个结构体的内存分布为:
type Test struct {bbooli3int32}ABBB B还记得之前说, CPU读取内存是一块一块读取的么? 而这个块, 假设是4B.
这样的话, 当你需要读取i3变量的时候, 需要进行两次内存访问. 而对齐之后, 只需要进行一次内存访问即可. 是典型的空间换时间的做法.
修改既然知道了问题出在哪里, 那么是不是如果换一下字段的存放顺序, 就可以压缩内存空间了呢? 思路很简单, 将对齐保证小的放到前面, 试一下:
type Test struct {bboolbybytei8int8i3int32i64 int64}func main() {t := Test{}fmt.Printf("%d", unsafe.Sizeof(t))}
文章插图
通过之前的对齐分析. 结果确为18B. 也就是因为字段顺序的问题, 编译器为了保证内存对齐, 向其中填充了很多空白, 造成了内存的浪费.
仅仅是修改了一下字段的顺序, 就可以将结构体的内存占用直接降低一倍. 见识了...
检测工具那么, 有没有什么办法能够帮我们检测是否存在内存对齐的优化呢? 毕竟平常写的时候, 谁会关心这玩意呢. 别说, 还真有. golangci-lint
官网: https://golangci-lint.run/
安装: brew install golangci-lint
检测所有文件命令: golangci-lint run ./..
检测一下最开始的结构体文件(添加参数指定检测内存对齐):
golangci-lint run --disable-all -E maligned main.go
看到结果:

文章插图
会看到提示, 该结构体当前占有32B, 可优化至16B. 完美.
【GO 内存对齐】当然, 此工具的功能不仅如此, 它能够提供很多建议, 有待发掘.
推荐阅读
- Linux怎么查看进程资源使用情况
- AMD|AMD Zen4锐龙7000抛弃DDR4内存!主板首次双芯片
- word表格文字如何居中对齐,Word表格文字怎样居中-
- 2rx8是几代内存条 pc2-6400s-666-12?内存条2rx8和DDR3
- 十分钟搞定分布式一致性算法
- WIN7上的极限操作:将已用内存控制在36MB
- 手机内存不足怎么清理?只需四步操作,让手机运转如飞
- 快速掌握Python中的循环技术
- MySQL还能实现分布式锁?
- 建议收藏 一文深度讲解JVM 内存分析工具 MAT及实践
