- 字节对齐
- unsafe.Sizeof
- unsafe.Offsetof
- 内存空洞
字节对齐
可以使计算机在加载和保存数据时,更加的有效率
通常情况下布尔和数字类型需要对齐到它们本身的大小(最多8个字节),其它的类型对齐到机器字大小
unsafe.Sizeof
返回操作数在内存中的字节大小,参数可以是任意类型的表,但不会对表达式进行求值(不求值也能知道大小,好神奇呀)
unsafe.Sizeof 返回的大小只包含数据结构中固定的部分。如果结构体含有指针字段,不包括针指向的内容。Go语言中非聚合类型通常有一个固定的大小,而聚合类型没有固定的大小,比如 结构体类型和数组类型
unsafe.Offsetof
函数的参数必须是一个字段 x.f,然后返回 f 字段相对于 x 起始地址的偏移量,包括可能的空洞
内存空洞
一个聚合类型(结构体或数组)的大小至少是所有字段或元素大小的总和,或者更大。因为可能存在内存空洞,内存空洞是编译器自动添加的没有被使用的内存空间,用于保证后面每个字段或元素的地址相对于结构或数组的开始地址能够合理地对齐。内存空洞可能会存在一些随机数据,可能会对用unsafe包直接操作内存的处理产生影响
结构体内存布局设:机器字大小为8个字节
产生的空洞
var x struct {
a bool
b int16
c []int
}
/* output
Sizeof(x) = 32 Alignof(x) = 8
Sizeof(x.a) = 1 Alignof(x.a) = 1 Offsetof(x.a) = 0
Sizeof(x.b) = 2 Alignof(x.b) = 2 Offsetof(x.b) = 2
Sizeof(x.c) = 24 Alignof(x.c) = 8 Offsetof(x.c) = 8
*/
xx.cc.data, c.len, c.capx.a + x.bx32 - (1 + 2 + 24) = 5x.cx.a + x.b = 3
字段偏移分析
Offsetof(x.a) = 0Offsetof(x.b) = 2Sizeof(x.a) = 1x.ab与ax.ax.ax.bx.c 与 x.bx.cx.c 与 x.b8 - 4 = 4
结构体字段顺序Go 语言中,结构内部字段的声明顺序和它们在内存中的顺序可能是不一样的。一个编译器可以随意地重新排列每个字段的内存位置,有效的包装可以使数据结构更加紧凑,从而节省内存空间
内存占用
不同结构体相同字段占用内存大小也会不一样,虽然 s1,s2,s3 有着相同的字段,但s1占用了较多的内存空间
var s1 = struct {a bool;b float64;c int16}{} // 3 words
var s2 = struct {a float64;b int16;c bool}{} // 2 words
var s3 = struct {a bool;b int16;c float64}{} // 2 words
s1占用空间
sizeof(s1) = 24 Alignof(s1) = 8
Sizeof(s1.a) = 1 Alignof(s1.a) = 1 Offsetof(s1.a) = 0
Sizeof(s1.b) = 8 Alignof(s1.b) = 8 Offsetof(s1.b) = 8
Sizeof(s1.c) = 2 Alignof(s1.c) = 2 Offsetof(s1.c) = 16
s1.a 与 s1.bs1.c与结构体结束处
所以: s1 总字节数是 1 + 8 + 2 + (7 + 6) 空洞 = 24 byte,即3个机器字,可以看出 s1 的字段与字段之间,排列的并不是很紧凑,有较大空洞,造成了内存的浪费
对齐方式:按一个机器字对齐的
|-a-|----------holes------------| 8字节,即一个机器字
|---------------b---------------| 8字节,即一个机器字
|---c---|---------holes---------| 8字节,也可看出是按一个机器字对齐的
s2 占用空间
sizeof(s2) = 16 Alignof(s2) = 8
Sizeof(s2.a) = 8 Alignof(s2.a) = 8 Offsetof(s2.a) = 0
Sizeof(s2.b) = 2 Alignof(s2.b) = 2 Offsetof(s2.b) = 8
Sizeof(s2.c) = 1 Alignof(s2.c) = 1 Offsetof(s2.c) = 10
s2.as2.bs2.c紧贴s2.bs2.c与结构体结束处
s28 + 2 + 1 + (5) 空洞 = 16 byte
对齐方式:按一个机器字对齐的
|---------------a---------------| 8字节,即一个机器字
|---b---|-c-|-------holes-------| 8字节,即一个机器字
s3占用空间
sizeof(s3) = 16 Alignof(s3) = 8
Sizeof(s3.a) = 1 Alignof(s3.a) = 1 Offsetof(s3.a) = 0
Sizeof(s3.b) = 2 Alignof(s3.b) = 2 Offsetof(s3.b) = 2
Sizeof(s3.c) = 8 Alignof(s3.c) = 8 Offsetof(s3.c) = 8
对齐方式:按一个机器字对齐的
s3 布局与 s2 相似,可以看成是上下两层对调了,但排列是很紧凑的,也是2个机器字
|-a-|---b---|-------holes-------| 8字节,即一个机器字
|---------------c---------------| 8字节,也可看出是按一个机器字对齐的
相关问题未来的Go语言编译器应该会默认优化结构体的顺序,当然应该也能够指定具体的内存布局,相同讨论请参考 Issue10014