Go结构体内存对齐
go的编译器按照指定的规则为结构体去分配内存,以及设置字段在内存中的位置
概念
对齐宽度
对齐宽度是类型的一种属性,他和类型本身以及操作系统有关。一般情况下,对齐宽度和类型大小是一致的。比如byte和bool类型的对齐宽度是1字节,int32类型对齐宽度是4字节。对齐宽度具有上限,在32位系统上,对齐宽度最大为4字节,因此,即便是int64类型,对齐宽度也是4字节,而不是8字节;相应的,在64位系统上,对齐宽度为8字节,即使是string(本身占16字节),对齐宽度也只有8字节。
本身所占字节数
类型指定的大小
实际占用字节数
类型大小+不满足对齐宽度规则时的填充字节数
偏移量
当前字段在内存中的字节编号相对于起始位置的偏移
规则
- 每个字段的偏移量必须是其对齐宽度的整数倍
- 最终结构体所分配的内存大小是最大齐宽度的整数倍
最大对齐保证:就是所有字段中最大的对齐宽度
内存对齐的好处
CPU是按照指定字长去读取内存的,内存对齐能够使CPU在一次内存读取操作完成对那些占用内存大小小于等于最大对齐宽度的结构体字段的读取。
- 合理的内存对齐可以提高内存读写的性能
- 便于实现变量操作的原子性
结论
- 结构体字段排列顺序可能最终的结构体大小不同
- 最终内存大小一定是所有字段中最大对齐宽度的整数倍
- 在构造结构体时候,按照内存大小有序排列,占用的空间更小.
有序时,由于内存对齐所产生的空白填充更小,空间占用更充分(有序:正序或者逆序)
例如:
{a byte, b int8, c int16, d int32, e int64} sum :16B
省略类型默认:int
{64 32 16 8 8 } sum 8+4+2+1+1=16B
{8 64 8 32 16 } sum 8+8+4+4+8=32B
{8 64 32 8 16 } sum 8+8+4+1+3=24B
正序,或者逆序,占用最小
package test
import (
"fmt"
"testing"
"unsafe"
)
//{8 64 8 32 16 }
type a struct {
f1 byte
f2 int64
f3 byte
f4 int32
f5 int16
}
func TestStructDuiQi(t *testing.T) {
var s = a{}
fmt.Println("st")
fmt.Println("对齐宽度:", unsafe.Alignof(s))
fmt.Println("本身大小:", unsafe.Sizeof(s))
fmt.Println("f1")
fmt.Println("对齐宽度:", unsafe.Alignof(s.f1))
fmt.Println("本身大小:", unsafe.Sizeof(s.f1))
fmt.Println("偏 移 量:", unsafe.Offsetof(s.f1))
fmt.Println("f2")
fmt.Println("对齐宽度:", unsafe.Alignof(s.f2))
fmt.Println("本身大小:", unsafe.Sizeof(s.f2))
fmt.Println("偏 移 量:", unsafe.Offsetof(s.f2))
}
output:
st
对齐宽度: 8
本身大小: 32
f1
对齐宽度: 1
本身大小: 1
偏 移 量: 0
f2
对齐宽度: 8
本身大小: 8
偏 移 量: 8
参考
https://blog.csdn.net/puss0/article/details/110007575
https://blog.csdn.net/weixin_32952295/article/details/112098106