go struct属性内存分配

308 阅读2分钟

go内存对齐机制

内存条基本组成

  • Rank是一组内存颗粒位宽的集合,也可以叫Chips,在PCB上,往往把一面上的内存颗粒组成一个Rank,另一面是另外一个Rank(假若有的话),这样也可以将Rank理解内存条的Side
  • 存储芯片,又叫内存颗粒。真正提供存储的器件
  • Chip里包含多个Bank,一个Bank就是一个存储矩阵库
  • ceil存储单元,可以保存1bit的数据,Bank由很多的Cell组成,由行列来确定一个Cell

cell.png

chip.png

内存数据读写

  1. 用m表示内存,m[i]表示rank,rank[j]表示chips,chips[k]表示某一bank,bank[z]表示某一行数据(假定8位存储)
  2. 内存控制器能够对同一个rank的所有chips同时进行读写操作,而在同一个rankchip也分享同样的控制信号 逻辑上连续的数据物理存储上并不连续,例如,a int64,可以这样存储: m[0][0][0][0]+m[0][1][0][0]+m[0][1][0][0]+.... 这意味着读取数据至少是8bit的整数倍,而64位的数据读取是并行从一系列chips下bank里面读取,而不是顺序存储和读取

内存对齐

内存对齐有两个要求:

  1. 内存对齐要求数据存起始储地址以及占用字节数都要是它对齐边界的倍数
  2. 存储struct的起始地址,是对齐边界的倍数 可以将对齐边界理解为类型长度(超过机器字长则取值为机器字长) index.jpg
import (
   "fmt"
   "unsafe"
)

func main()  {
   type T1 struct {
      a int16
      b int64
      c string
   }
   type T2 struct {
      a int16
      b int64
      c string
   }
   type T3 struct {
      a int16
      b int64
      c string
   }
   t1:=&T1{1,2,"123"}
   fmt.Println(unsafe.Alignof(t1))  // struct本身存储的偏移量8
   fmt.Println(unsafe.Sizeof(*t1))  // 32

   fmt.Println(unsafe.Alignof(t1.a)) //  2
   fmt.Println(unsafe.Sizeof(t1.a))  //  2
   fmt.Println(unsafe.Offsetof(t1.a)) // 0

   fmt.Println(unsafe.Alignof(t1.b)) //  8
   fmt.Println(unsafe.Sizeof(t1.b))  //  8
   fmt.Println(unsafe.Offsetof(t1.b)) // 8

   fmt.Println(unsafe.Alignof(t1.c)) //  8
   fmt.Println(unsafe.Sizeof(t1.c))  //  16
   fmt.Println(unsafe.Offsetof(t1.c)) // 16

}

三种不同的struct在内存中结构完全不一致,不同的组合会导致struct结构体占用内存大小不同,实现排序占用内存最小算法,本身太复杂,最简单的情况如下:

// 结构体只有两个属性a,b
function(a,b int)(c,d int){
   if(a-8>=0|| b-8>=0){
      return a,b
   }else if(a==b){
      return a,b
   }else if(a+b>=8){
       return a,b    
   }else {
   // 假定b>a
   temp1:= b-a 
   n:= (b+a-1)/a;
   temp2:= a*n -b
   }
   return tem1>temp2?a,b:b,a
}