• Golang内存对齐


    什么是内存对齐?

      编译器会将数据按照特定的规则,把数据安排到合适的存储地址上,并占用合适的地址长度
    
    • 1

    为什么要内存对齐

      保证程序顺利高效的运行,可以让CPU快速从内存中存取到字段,避免资源浪费
    
    • 1

    内存对齐规则

         1、起始的存储地址 必须是 内存对齐边界 的倍数。
         2、整体占用字节数 必须是 内存对齐边界 的倍数。

    Tip:先声明两个概念 ↓ ↓
          内存对齐边界:结构体所有元素中,哪个元素占用的字节数大,那么这个元素占用的字节数就是内存对齐边界
          对齐边界:结构体中每个元素自己占用的字节数

    通过下边的示例来理解内存对齐的规则

         首先我们定义一个结构体:

    type S struct {
       A uint8     // byte:1
       B int32     // byte:4
       C int16     // byte:2
       D int64     // byte:8
       E [2]string // byte总和:32 -->string类型数组,总共2个元素,每个元素包含2部分内容:
       			   // 内容1:ptr,指向存放数据的地址,byte:8:  内容2:len,标识字符串长度的整数值,byte:8
       F struct{}  // zero size field
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    步骤一:确定内存对齐边界

        统计出结构体中所有的元素分别占用的字节数,占用最大的字节数就是内存对齐边界,在这个示例中的内存对齐边界就是 8
        Tip:如果不知道怎样确定内存对齐边界,可以使用unsafe.Alignof()函数打印每个元素的对齐系数,打印中的最大值就是内存对齐边界:

    func main() {
    	fmt.Println(unsafe.Alignof(S{}.A))   // output: 1
    	fmt.Println(unsafe.Alignof(S{}.B))  // output: 4
    	fmt.Println(unsafe.Alignof(S{}.C))  // output: 2
    	fmt.Println(unsafe.Alignof(S{}.D))  // output: 8
    	fmt.Println(unsafe.Alignof(S{}.E))  // output: 8
    	fmt.Println(unsafe.Alignof(S{}.F))  // output: 1
    	
    	fmt.Println(unsafe.Sizeof(S{}))		//可以直接打印出结构体所占用的字节数
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    步骤二:确定起始存储地址
        内存对齐规则第一条:起始的存储地址 必须是 内存对齐边界 的倍数。也就是(起始地址addr)%(内存对齐边界)=0,在我们示例中起始地址就是addr%8=0,我们从0地址开始,为了方便看,我们先用图来展示地址存储分布图

    在这里插入图片描述

    起始地址为0,0%8=0,符合条件,那我们就从0开始存储数据

    其中元素A占用1个字节,并且0%1=0,符合条件,第0个地址分配给A;
           元素B占用4个字节,从1个地址分配的话,1%4≠0,只有4%4=0,所以把第4-7个地址分配给B;
           以此类推······ 具体分配如下

      元素A:1个字节,占用第0个相对地址空间,      0%1 =0(起始地址为0,内存边界为1)
      元素B:4个字节,占用第4-7个相对地址空间,    4%4 =0(起始地址为4,内存边界为4)
      元素C:2个字节,占用第8-9个相对地址空间,    8%2 =0(起始地址为8,内存边界为2)
      元素D:8个字节,占用第16-23个相对地址空间,  16%8=0(起始地址为16,内存边界为8)
      元素E:8*2*2个字节,占用第24-31和32-39和40-47和48-55个相对地址空间,24%8=0...(起始地址为24,内存边界为8...)
      元素F:zero size field,占用第56个相对地址空间,	 56%1=0 (起始地址为56,内存边界为1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

           最终内存在第56个相对地址空间分配完毕,但是我们的地址空间是从0开始计算的,所以目前来看结构体总共占用57个字节!

    步骤三:确定结构体占用字节数

           我们在步骤二中排列出了结构体中元素的存放地址,整体元素占用57个字节,但是到这里还不算完事儿,因为我们还没有执行第二条的内存对齐规则–>整体占用字节数 必须是 内存对齐边界 的倍数。我们的内存对齐边界为8,而57不是8的倍数,所以我们需要扩张字节空间到8的倍数,延伸到64,也就是扩张到到图中的第63个相对地址空间。这个结构体占用的字节数为64字节!

    内存空间优化

           通过上边的示例与图表我们不难看出,其中还有好多个地址空间被浪费掉了,这些没被利用的地址空间,go语言会进行padding操作来对这些空间进行填充,使这些空间变成合法的内存空间。

           我们再思考一下,如何才能减少地址空间的浪费呢?能不能通过重新排列元素的位置来合理的分配地址空间呢?
    答案当然是肯定的,我们可以通过合理排列元素的定义顺序来减少地址空间的浪费。

           我们先看结论,下边是重新排列后的结构体:

    type S1 struct {
    	A uint8
    	F struct{}
    	C int16
    	B int32
    	D int64
    	E [2]string
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

           再看一下重新分配地址空间的图标:

    在这里插入图片描述

           起始地址为0,0%8=0,符合条件,我们就从0开始存储数据

    其中元素A占用1个字节,并且0%1=0,符合条件,第0个地址分配给A;
           元素F占用1个字节,1%1=0,符合条件,将第1个地址分配给B;
           以此类推······ 具体分配如下

      元素A:1个字节,占用第0个相对地址空间,      	0%1 =0(起始地址为0,内存边界为1)
      元素F:zero size field,占用第1个相对地址空间,1%1 =0(起始地址为1,内存边界为1)
      元素C:2个字节,占用第2-3个相对地址空间,	    2%2 =0(起始地址为2,内存边界为2)
      元素B:4个字节,占用第4-7个相对地址空间,	    4%4=0(起始地址为4,内存边界为4)
      元素D:8个字节,占用第8-15个相对地址空间,		8%8=0(起始地址为8,内存边界为8)
      元素E:8*2*2个字节,占用第16-23和24-31和32-39和40-47个相对地址空间,16%8=0... (起始地址为16,内存边界为8...)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

           最终内存在第47个相对地址空间分配完毕,但是我们的地址空间是从0开始计算的,所以目前来看结构体总共占用48个字节!并且48还是内存对齐边界值8的整数倍,所以结构体最终占用48个字节!

           我们可以很明显的看出来,在我们改变元素的定义顺序后,占用的字节空间从64字节减少到了48字节,内存空间得到了充分的优化!!!这也是一个结论所在,我们在结构体定义变量的时候,尽量将相同类型的变量定义在一起,将占用字节较少的变量类型放在一块。这也只是我个人的一个结论,其中内容有问题的话,欢迎大家进行指正!感谢您的阅读!

  • 相关阅读:
    SSM甜品小屋网上订餐系统的设计与实现毕业设计-附源码151018
    源码编译elfutils
    宝塔面板用exe安装,不要用网页安装
    django基于python的编制考试信息管理系统--python-计算机毕业设计
    入门力扣自学笔记114 C++ (题目编号623)
    Mybatis-Plus实现乐观锁
    面试官:你确定 Redis 是单线程的进程吗?
    基于Prometheus的容器云平台监控架构设计
    【网络协议】聊聊ICMP与ping是如何测试网络联通性
    【进阶玩法】策略+责任链+组合实现合同签章
  • 原文地址:https://blog.csdn.net/weixin_43871135/article/details/125497020