先执行一段示例代码,有一个类似如下的结构体 myStruct
:
package main
import (
"fmt"
"reflect"
"unsafe"
)
type myStruct struct {
myBool bool
myFloat float64
myInt int32
}
func main() {
a := myStruct{}
fmt.Println(unsafe.Sizeof(a.myBool)) // 1
fmt.Println(unsafe.Sizeof(a.myFloat)) // 8
fmt.Println(unsafe.Sizeof(a.myInt)) // 4
fmt.Println(unsafe.Sizeof(a)) // 24
fmt.Println(reflect.TypeOf(a.myBool).Align()) // 1
fmt.Println(reflect.TypeOf(a.myFloat).Align()) // 8
fmt.Println(reflect.TypeOf(a.myInt).Align()) // 4
fmt.Println(reflect.TypeOf(a).Align()) // 8
fmt.Println(unsafe.Offsetof(a.myBool)) // 0
fmt.Println(unsafe.Offsetof(a.myFloat)) // 8
fmt.Println(unsafe.Offsetof(a.myInt)) // 16
}
通过 Sizeof 输出的大小发现,虽然结构体各字段 bool 占用 1 字节,float64 占用 8 字节,int32 占用 4 字节,但是总共大小却是 24 字节。
在实例化结构体的时候,使用的内存并不是 1 + 8 + 4 = 13
字节,
实际上在分配内存的时候,有一个内存对齐规则,结构体是按最大的类型需要的 8 字节为基础进行内存对齐的分配,超过 8 字节时必须独占,不同的机器可能不同。
所以第一个 bool 字段分配 8 字节的空间,第一个字节放入 myBool,这时 myFloat float64 需要 8 个字节,剩下的 7 个字节不够放,所以 Offset 到下一个 8 字节的空间,于是这两个字段就用了 16 字节,依次类推,所以每个字段都用了 8 字节,所以是 8 + 8 + 8 = 24
字节。
内存分配如图:
|X|O|O|O|O|O|O|O| bool 分配了8字节的空间 使用1字节
|X|X|X|X|X|X|X|X| float64 分配了8字节的空间 使用8字节
|X|X|X|X|O|O|O|O| int32 分配了8字节的空间 使用4字节
如何优化:
内存对齐影响 struct 的大小,struct 的字段顺序影响 struct 的大小。
调整结构体字段的顺序就可以优化,这里将最大字节的类型放到最前面即可:
type myStruct struct {
myFloat float64
myBool bool
myInt int32
}
func main() {
a := myStruct{}
fmt.Println(unsafe.Sizeof(a.myBool)) // 1
fmt.Println(unsafe.Sizeof(a.myFloat)) // 8
fmt.Println(unsafe.Sizeof(a.myInt)) // 4
fmt.Println(unsafe.Sizeof(a)) // 16
fmt.Println(reflect.TypeOf(a.myBool).Align()) // 1
fmt.Println(reflect.TypeOf(a.myFloat).Align()) // 8
fmt.Println(reflect.TypeOf(a.myInt).Align()) // 4
fmt.Println(reflect.TypeOf(a).Align()) // 8
fmt.Println(unsafe.Offsetof(a.myFloat)) // 0
fmt.Println(unsafe.Offsetof(a.myBool)) // 8
fmt.Println(unsafe.Offsetof(a.myInt)) // 12
}
此时内存分配方式发生了变化:
第一个 float64 刚好占满 8 个字节的空间,第二个 bool offset到第二个 8 字节空间即第 8+1 个字节存放, 这里第三个 int32 是 offset 到了第 12+1 个字节的位置存放,应该是 int32 自身的内存对齐是4个字节的原因
内存分配变为:
|X|X|X|X|X|X|X|X| float64 分配了8字节的空间 使用8字节
|X|O|O|O|X|X|X|X| bool和int32 共用第二段8字节的空间
这样调整结构体的字段顺序后,只需两块连续的8字节就能保存全部字段,只需要16字节。