Go结构体之内存分配

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中字段的声明顺序对内存占用产生影响,调整字段声明顺序可以优化内存占用,这里将内存占用更大的类型更早声明即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

uncle_mooncake

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值