阿小信大人的头像
Life is short (You need Python) Bruce Eckel

Go中组织结构体以节省内存2018-12-11 15:51

Go中组织结构体以节省内存

有一个类似如下的结构体 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
}

虽然结构体字段bool占用1字节,float64占用8字节,int32占用4字节。 但是在实例化结构体的时候,使用的内存并不是1 + 8 + 4 = 13字节, 实际上在分配内存的时候,有一个内存对齐规则,在这里可以看到结构体是按最大的类型需要的8字节为基础进行内存对齐的分配,超过8字节时必须独占,不同的机器可能不同。 所以第一个bool字段分配8字节的空间,第一个字节放入myBool,这时float64需要8个字节,剩下的7个字节不够放,所以Offset到下一个8字节的空间,依次类推,所以每个字段都是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
}

此时内存分配方式发生了变化:

第一个float32刚好占满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|    boolint32 共用第二段8字节的空间

这样排序结构体的字段后,只需两块连续的8字节就能保存全部字段,所以只需要16字节。

如果您觉得从我的分享中得到了帮助,并且希望我的博客持续发展下去,请点击支付宝捐赠,谢谢!

若非特别声明,文章均为阿小信的个人笔记,转载请注明出处。文章如有侵权内容,请联系我,我会及时删除。

#Golang#  
分享到:
阅读[420] 评论[0]

下一篇:已经是最后一篇

你可能也感兴趣的文章推荐

本文最近访客

网友54.*.*.116[火星]2019-04-25 06:28
网友46.*.*.36[火星]2019-04-25 06:19
网友66.*.*.63[Mountain View]2019-04-25 05:19
网友46.*.*.141[火星]2019-04-25 05:08

发表评论