1.关键词
内存对齐、偏移量、对齐值。
2.内存对齐的规则
内存对齐的规则总体看来分为两部分,这两部分是建立在linux系统的位数的基础之上的。操作系统可以分为32位和64位操作系统,与此相对应,内存的IO是以4字节和8字节为单位进行的,若不采用内存对齐,在读取特定数据时可能导致在总线上进行两次读取。由于上述原因,32位和64位系统下编译器默认的对齐值分别为4字节和8字节。
内存对齐的第一个规则就是在默认设置的对齐值和struct内某一字段的内存占用大小值两个数值之中取最小值作为该字段的对齐值,即对齐值=min(编译器默认值,sizeof(字段类型))。
内存对其的第二个规则就是对整个结构体而言也要进行对齐,对齐值取默认值和结构体中字段占用内存最大值两者中的最小值,即对齐值=min(默认值,字段最大内存占用值)。
3.示例演示
package main
import (
"fmt"
"unsafe"
)
type user1 struct {
b int8 //int8 => 8bit => 1byte
i int32 //int32=>32bit => 4byte
j int64 //int64=>64bit => 8byte
}
type user2 struct {
b byte
j int64
i int32
}
type user3 struct {
i int32
b byte
j int64
}
type user4 struct {
i int32
j int64
b byte
}
type user5 struct {
j int64
b byte
i int32
}
type user6 struct {
j int64
i int32
b byte
}
func main() {
var u1 user1
var u2 user2
var u3 user3
var u4 user4
var u5 user5
var u6 user6
//unsafe.sizeof()可以求得struct所占用内存大小
fmt.Println("u1 size is ", unsafe.Sizeof(u1))
fmt.Println("u2 size is ", unsafe.Sizeof(u2))
fmt.Println("u3 size is ", unsafe.Sizeof(u3))
fmt.Println("u4 size is ", unsafe.Sizeof(u4))
fmt.Println("u5 size is ", unsafe.Sizeof(u5))
fmt.Println("u6 size is ", unsafe.Sizeof(u6))
}
上述示例的输出结果如下:
u1 size is 16
u2 size is 24
u3 size is 16
u4 size is 24
u5 size is 16
u6 size is 16
同时unsafe包中的Offsetof函数和Alignof函数能够获取结构体中某一字断的偏移量和对齐值,对上述对齐规则进行佐证,用法如下:
//偏移值
unsafe.Offsetof(u1.b) //偏移量0
unsafe.Offsetof(u1.i) //偏移量16
//对齐量
unsafe.Alignof(u1.b)
4.示例分析
首先,根据上述第一条规则,可以分别求得结构体每一个字段的对齐量,而偏移值距离已占用内存最近且必须为对齐量的整数倍。此处注意偏移值从0开始,0可以看作任意对齐量的整数倍。
对于struct user1来说:
type user1 struct {
b int8 //对齐量:min(8, 1)=1,偏移量:0,占据0
i int32 //对齐量:min(8, 4)=4,偏移量:4(之所以为4是因为比0大且为4的倍数的最小值为4),占据4、5、6、7
j int64 //对齐量:min(8, 8)=8,偏移量:8(之所以为8是因为比7大且为8的倍数的最小值为8),占据8、9、10、11、12、13、14、15
}
//所以根据第一条规则struct user1内存占用为0-15共16字节
对于struct user2来说:
type user2 struct {
b byte //对齐量:min(8, 1)=1,偏移量:0,占据0
j int64 //对齐量:min(8, 8)=8,偏移量:8(之所以为8是因为比0大且为8的倍数的最小值为8),占据8、9、10、11、12、13、14、15
i int32 //对齐量:min(8, 4)=4,偏移量:16(之所以为16是因为比15大且为4的倍数的最小值为16),占据16、17、18、19
}
//所以根据第一条规则struct user2内存占用为0-19共20个字节
然后,根据第二条规则,对齐量还要针对整个struct进行计算,对于user1和user2而言,其中字段的最大内存占用均为8,所以对齐量均=min(8, 8)=8,偏移值均必须为8的倍数。user1占用内存大小为16,满足此规则,而user2占用内存为20,不符合此规则,必须填充4字节内存至24字节方可满足,所以满足上述两个规则的结构体user1和user2的内存占用分别为16和24,其余struct可依此思路进行分析计算。下面分别表示user1和user2的内存占用情况:
//user1
bxxx|iiii|jjjj|jjjj
//user2
bxxx|xxxx|jjjj|jjjj|iiii|xxxx
5.结论及优化
由此可知,内存对齐规则的存在使得struct中字段的声明顺序对内存占用产生影响,调整字段声明顺序可以优化内存占用,这里将内存占用更大的类型更早声明即可。