Go struct 内存对齐 | Go 语言高性能编程 | 极客兔兔
geektutu opened this issue · comments
https://geektutu.com/post/hpg-struct-alignment.html
Go 语言/golang 高性能编程,Go 语言进阶教程,Go 语言高性能编程(high performance go)。本文介绍了结构体(struct)占用的内存空间如何计算,为什么要字节对齐/内存对齐,Go 语言中的大小和对齐保证(size and alignment guarantees),字节对齐和安全访问(原子访问)的关系,以及如果利用内存对齐的规律减小 struct 内存占用。
如果内存对齐只是涉及更改顺序的话,go 为什么不在编译时和运行时自动进行对齐呢?
如果编译时存在跨平台优化问题,那么运行时应该可以确定如何对齐最优吧。
@OhYee
如果内存对齐只是涉及更改顺序的话,go 为什么不在编译时和运行时自动进行对齐呢?
如果编译时存在跨平台优化问题,那么运行时应该可以确定如何对齐最优吧。
Go在编译的时候会自动内存对齐,就是上面说的,不同顺序占用的内存不一样,这就是自动对齐的结果。Go如果更改了结构体的顺序,如果在数据传输的时候,如何正确的读取到结构体的内容呢?
字节对齐这个概念有两处需要强调:
- CPU 只从对齐的地址开始加载数据(这一点可能遗漏了)
- CPU 读取块的大小是固定的,通常为 B 的 2 的整数幂次
“b 是第二个字段,对齐倍数为 2,因此,必须空出 1 个字节,偏移量才是 2 的倍数,从第 2 个位置开始占据 2 字节。”
为什么要空一个字段呢?
@qilinworld
“b 是第二个字段,对齐倍数为 2,因此,必须空出 1 个字节,偏移量才是 2 的倍数,从第 2 个位置开始占据 2 字节。”
为什么要空一个字段呢?
为了减少CPU读取次数,是一种空间换时间的做法。
比如demo1,按照2的倍数,第一次读取a(实际占用1字节)读两个字节,第二次读取b(实际占用2字节)也是两个字节,第三次读取c(实际占用4个字节)前两个字节,第四次读取c后两个字节,共读取4次。
demo2也是,按照c的倍数4对齐,第一次读取a(实际占用1字节)四个字节,第二次读取c四个字节,第三次读取b(实际占用2字节)四个字节,共读取3次。
如果按照a的1倍对齐,那么demo1里CPU就要读取a(1次)+b(2次)+c(3次)=6次,demo2也一样。
但我在本机看到demo1和demo2的对齐倍数都是4
对于 struct 结构体类型的变量 x,计算 x 每一个字段 f 的 unsafe.Alignof(x.f),unsafe.Alignof(x) 等于其中的最大值。
根据上面的规则,demo1中的b字段的对齐:b 是第二个字段,对齐倍数为 2,因此,必须空出 1 个字节,偏移量才是 2 的倍数,从第 2 个位置开始占据 2 字节。
应该将对齐倍数看做4才对吧?
空结构体填充不是字节对齐的问题
"即当 struct{} 作为结构体最后一个字段时,需要内存对齐。因为如果有指针指向该字段, 返回的地址将在结构体之外,如果此指针一直存活不释放对应的内存,就会有内存泄露的问题(该内存不因结构体释放而释放)。"
这段话不太理解,“因为如果有指针指向该字段, 返回的地址将在结构体之外”,是对齐的时候,还是非对齐的时候产生此现象。以及为什么返回的地址在结构体之外?能否举个具体的例子呢?
@HeliumTang
对于 struct 结构体类型的变量 x,计算 x 每一个字段 f 的 unsafe.Alignof(x.f),unsafe.Alignof(x) 等于其中的最大值。
根据上面的规则,demo1中的b字段的对齐:b 是第二个字段,对齐倍数为 2,因此,必须空出 1 个字节,偏移量才是 2 的倍数,从第 2 个位置开始占据 2 字节。
应该将对齐倍数看做4才对吧?
到b字段的时候最大值是b只占用2字节没问题的,到c字段最大值为c占用4字节没问题的
博主,请问您的这个博客站点是如何搭建的?
有联系方式吗
兔兔, 这个地方感觉有点模糊,
- 泄露的是哪块内存,
- 为什么补齐之后就不会发生泄露了
因为如果有指针指向该字段, 返回的地址将在结构体之外,如果此指针一直存活不释放对应的内存,就会有内存泄露的问题(该内存不因结构体释放而释放)。
- 我理解的是返回的地址指的是空结构体的地址, 也就是zerobase
- 如果1成立, 那么有很大概率这个地址会永远存在, 因为这个地址是所有空值公用的
- 如果不是zerobase, 那返回的是什么地址
我用fieldalignment检测,并没有提示demo2需要优化,是什么原因呢