Golang是一种C家族的语言,关于内存对齐和C有大部分相通的地方。如果之前就对内存对齐有了解的话,很容易理解。
先看一个例子吧
package main
import (
"fmt"
"unsafe"
)
type part struct {
a bool
b int32
c int8
d int64
e byte
}
func main() {
part1 := part{}
fmt.Printf("bool size: %d, align: %d, offset: %d\n", unsafe.Sizeof(part1.a), unsafe.Alignof(part1.a), unsafe.Offsetof(part1.a))
fmt.Printf("int32 size: %d, align: %d, offset: %d\n", unsafe.Sizeof(part1.b), unsafe.Alignof(part1.b), unsafe.Offsetof(part1.b))
fmt.Printf("int8 size: %d, align: %d, offset: %d\n", unsafe.Sizeof(part1.c), unsafe.Alignof(part1.c), unsafe.Offsetof(part1.c))
fmt.Printf("int64 size: %d, align: %d, offset: %d\n", unsafe.Sizeof(part1.d), unsafe.Alignof(part1.d), unsafe.Offsetof(part1.d))
fmt.Printf("byte size: %d, align: %d, offset: %d\n", unsafe.Sizeof(part1.e), unsafe.Alignof(part1.e), unsafe.Offsetof(part1.e))
fmt.Printf("part1 size: %d, align: %d\n", unsafe.Sizeof(part1), unsafe.Alignof(part1))
}

由结果可以看到bool int8 int32 int64 byte的大小,但是相加的结果并不是part结构体的结果,从结果可以看出来,bool占用了一个字节后,下一个int32类型占用4字节时是有4个字节偏移的,所以一共占用了八个字节,向后推导发现有些类型占用了比类型本身字节更大的空间。导致这个问题的原因就是内存对齐。
1. 为什么会有内存对齐这回事
大部分资料都在说两个原因
- 平台问题:并不是所有的硬件平台都能访问任意地址上的任意数据。
- 性能问题:访问未对齐内存需要cpu进行两次访问,对齐后只需要一次。
2. 内存对齐原则
- 对齐值为系统默认对齐值和类型大小长度的最小值
- 结构体内部字段对齐值为默认对齐值和字段最大类型长度的最小值。
3. 内存对齐的使用
由于内存对齐的存在,使得类型按照不同的顺序排列属性可能会得到不同的大小,所以在受限条件下设计类型时需要注意。
package main
import (
"fmt"
"unsafe"
)
type part1 struct {
a bool
b int32
c int8
d int64
e byte
}
type part2 struct {
a bool
e byte
c int8
b int32
d int64
}
func main() {
p1 := part1{}
p2 := part2{}
fmt.Printf("part1 size: %d\n", unsafe.Sizeof(p1))
fmt.Printf("part2 size: %d\n", unsafe.Sizeof(p2))
}

从结果可以看到,顺序不用对类型体积还是有影响的。下面是对类型内存占用的示意图,-表示占空。
p1 : |a---|bbbb|c---|----|dddd|dddd|e---|----|
p2 : |aec-|bbbb|dddd|dddd|
从上面的示意可以看出part1和part2的占用方式,p2相比p1减少了一半属性占用空间。
参考材料:
1. https://go101.org/article/memory-layout.html
2. https://blog.csdn.net/u011957758/article/details/85059117
3. https://studygolang.com/articles/15540?fr=sidebar
记录每天解决的小问题,积累起来去解决大问题。 |