美文网首页
golang的内存对齐

golang的内存对齐

作者: Stevennnmmm | 来源:发表于2021-04-20 16:42 被阅读0次

    什么是内存对齐

    type person struct {
        age int64
        height int64
    }
    func TestMdemo(t *testing.T) {
        fmt.Println(unsafe.Sizeof(person{}))
        fmt.Println(person{})
    }
    

    此时的 打印值为:16,一个64长度int占8字节,两个就是16。

    type person struct {
        age int32
        height int64
    }
    func TestMdemo(t *testing.T) {
        fmt.Println(unsafe.Sizeof(person{}))
        fmt.Println(person{})
    }
    

    此时的 打印值为:16,4+8 =12 ,和我们预期不符合。这就是内存对齐现象。

    为什么要对齐

    那么关于golang如何对齐内存的呢。这里和cpu的访问数据是有关的。
    cup访问内存并非按字节读取,而是按照 ”字长“ 来读取

    计算机功能的强弱或性能的好坏,不是由某项指标决定的,而是由它的系统结构、指令系统、硬件组成、软件
    配置等多方面的因素综合决定的。
    
    计算机以及其相关的软件、硬件设备作为现代科学技术的结晶。应用于工业、商业、金融、教育、科研、军
    事、通信及国防建设等国民经济的各个方面。诸多行业的发展对计算机科学愈来愈表现出强烈的依赖性。所以
    计算机的评价与检测就十分重要。计算机功能的强弱或性能的好坏,不是由某项指标决定的,而是由它的系统
    结构、指令系统、硬件组成、软件配置等多方面的因素综合决定的。
    
    计算机在同一时间内处理的一组二进制数称为一个计算机的“字”,而这组二进制数的位数就是“字长”。在其他
    指标相同时,字长越大计算机处理数据的速度就越快。早期的微型计算机的字长一般是8位和16位。586
    (Pentium, Pentium Pro, PentiumⅡ,PentiumⅢ,Pentium 4)大多是32位,大多数人都装64位的了。
    

    一般我们现在的服务器都是64位8字节的字长。那么实际上我们cpu读取一个8字节的数据只需要一次读取。

    • 1.增加程序的吞吐
      cpu读取一个变量是由字长开始读取,那么我读取int64的话只需要读一次,如果按照字节长度来读取的话实际上就不太可行,需要读取8次获取一个数据,操作明显的累赘。但是如果说字长太长实际上又会导致部分的空间浪费,为了利于服务器运行并发程序,需要有一个合适的值。
    • 2.利于并发的原子性操作
      我们如何来利用cpu天然的利好与并发程序呢,此时字长就很重要。举个简单例子:我有一个公共变量 counter int64,如果我在不加锁的情况下(我并不在乎最后结果如何),多个线程对值进行改变,此时得到的具体值我们并不能估计出来,但是我们可以知道这个值的内存分布式是正确的。也就是一个single mechine word。 所以这个是一个”并发安全“的操作。大家可以想一下,因为这个值每次只能一次性读出来,一次cpu进行修改,所以在多核线程上世纪就有了一个并发”安全“的操作。此处安全是打引号。我不反对但也不建议这么操作,从业务角度来说你肯定不能对值进行预期。

    如何对齐 (对齐系数)

    Size and alignment guarantees

    type                                 size in bytes
    byte, uint8, int8                     1
    uint16, int16                         2
    uint32, int32, float32                4
    uint64, int64, float64, complex64     8
    complex128                           16
    

    保证”结构体“最小对齐

        For a variable x of any type: unsafe.Alignof(x) is at least 1.
        For a variable x of struct type: unsafe.Alignof(x) is the largest of all the values unsafe
        .Alignof(x.f) for each field f of x, but at least 1.
        For a variable x of array type: unsafe.Alignof(x) is the same as the alignment of a variable of the array's element type.
    
    • 任意变量的最小align 系数 >=1
    • 任意结构体变量 ,align系数是其实 struct 中所有参数 align 值的最大一位
    • 对于数组变量:就是单个数组值的align值

    其实掌握了align 系数算法我们可以轻松算出struct 的size

    利用对齐系数计算结构体大小

    我们首先背下来官网的三个原则,就是上面写好了的。

    • 一般来说一个非结构体 的值,string不算,底层是字节数组,它的align =size
    • 一个结构体的align = align_max(struct.filed)
    • align值最小为1

    我们来简单计算一下

    type person struct {
        i int32
        i1 int32
    }
    
    size = size(i) +size(i1) : 8
    align = align(i)/align(i1) :4  此时因为两个值的align相同
    
    type person struct {
        i int32
        i1 int32
        i2 int64
    }
    
    size = 16      此时应该是size i +size i1 +size i2 = 4+4+8 =16
    align = align(i2):8  此时因为两个值的align相同
    
    type person struct {
        i int32
        i1 int64
    }
    
    size = 16      此时应该是size i +size i1  = 4+8 =12 ,但是应该size是 align 的倍数,所以最近就是16
    align = align(i2):8  此时因为两个值的align相同
    

    计算中的特殊情况

    type person struct {
        i int32
        a struct{}
    }
    size = 8     
    align = align(i2):4  此时取值align(4)
    

    这里我们需要特殊说明一下情况:假设a 字段为空,那么如果这个结构体有个方法是返回a,具体如下

    type person struct {
        i int32
        a struct{}
    }
    func (a *person)ted()interface{}{
        return a.a
    }
    

    这种情况就有可能出现内存泄露的情况。

    相关文章

      网友评论

          本文标题:golang的内存对齐

          本文链接:https://www.haomeiwen.com/subject/utjilltx.html