美文网首页
002-内存对齐

002-内存对齐

作者: 可可先生_3083 | 来源:发表于2021-07-08 17:34 被阅读0次

    引子

    前面我们探索了对象的内存空间分配的alloc函数。通过align16和align8计算对象所需内存大小。实际上每次都会走align16 的cacheFastInstance(在realizeClassWithoutSwift对cache做了赋值)。这里align16就是做的事情就是对instace的存储做了内存对其齐操作。

    什么是内存对齐

    • 内存对齐应该是编译器的“管辖范围”。编译器为程序中的每个“数据单元”安排在适当的位置上。但是C语言的一个特点就是太灵活,太强大,它允许你干预“内存对齐”。如果你想了解更加底层的秘密,“内存对齐”对你就不应该再模糊了。

    程序员通常倾向于认为内存就像一个字节数组.在C及其衍生语言中,char * 用来指代"一块内存",甚至在JAVA中也有byte[]类型来指代物理内存.


    image.png

    Figure 1. ****程序员是如何看内存的

    然而,你的处理器并不是按字节块来存取内存的.它一般会以双字节,四字节,8字节,16字节甚至32字节为单位来存取内存.我们将上述这些存取单位称为内存存取粒度.

    image.png

    内存对齐的规则

    结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。在iOS中,数据成员的offeset是自身长度的整数倍。(说明默认的#pragma pack >= 8)

    • 结构(或联合)的整体对齐规则:

    在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。iOS中,结构体大小是最大数据成员长度的整数倍,如有需要,会在最后一个成员之后加上填充字节(trailing padding

    • 结构(或联合) 作为子成员 offset为自身最大数据成员长度的整数倍
    • 结合上述可推断:

    当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。数据成员以自身长度为对齐单位

    为什么要内存对齐

    1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。数据要按一定规则去存取,有些硬件平台不兼容不对齐数据,访问会抛出异常,所以大家最好都按一定的规则去存取数据
    2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。经典示例

    image.png
    image.png
    上述示例中,内存对齐,cpu只需要一次内存访问就可以获取想要的数据,不内存对齐需要访问俩次,还要做数据的剔除整合,显然大大消耗cpu性能。显然,内存对齐能大大提升cpu的访问效率,节省cpu性能,当然这回带来一些额外的内存开销

    几个例子

    写几个结构体,真机debug下,直观便于理解。 截屏2021-07-08 下午6.06.14.png

    验证几个点:

    1. 数据成员的offset是自身长度的整数倍(必要时会mid padding)
    2. 结构体作为子成员offset是自己最大数据成员的长度的整数倍
    3. 结构体自身大小是自己最大数据成员长度的整数倍(必要时 trail padding)

    iOS中的实例对齐

    通过结构体的内存对齐规则,我们可以计算得到一个instance需要的内存大小。然而在上篇的alloc函数中,在计算instance需要分配的空间时,会再次进行align16对齐,也就是16字节对齐。比如我们NSObject对象 截屏2021-07-08 下午5.55.47.png

    需要8,实际分配16

    为什么采用16字节对齐

    苹果采取16字节对齐,是因为OC的对象中,第一位叫isa指针,它是必然存在的,而且它就占了8位字节,就算你的对象中没有其他的属性了,也一定有一个isa,那对象就至少要占用8位字节。如果以8位字节对齐的话,如果连续的两块内存都是没有属性的对象,那么它们的内存空间就会完全的挨在一起,是容易混乱的。

    image.png
    1.以16字节对齐保证OC对象内存之间不是完全挨在一起,在进行instance数据访问的时候不容易出错
    2.保证对象内存连续整齐,提升cpu的访问效率,节省cpu性能

    算法

    static inline size_t align16(size_t x) {
        return (x + size_t(15)) & ~size_t(15);
    }
    

    lib.malloc的内存对齐

    调用malloc方法分配内存空间,会再次进行对齐

    #define SHIFT_NANO_QUANTUM      4
    #define NANO_REGIME_QUANTA_SIZE (1 << SHIFT_NANO_QUANTUM)   // 16
    
    static MALLOC_INLINE size_t
    segregated_size_to_fit(nanozone_t *nanozone, size_t size, size_t *pKey)
    {
        size_t k, slot_bytes;
        if (0 == size) {
            size = NANO_REGIME_QUANTA_SIZE; // Historical behavior
        }
            //k = (size + 16 - 1) >> 4 左移4位
        k = (size + NANO_REGIME_QUANTA_SIZE - 1) >> SHIFT_NANO_QUANTUM;
            // round up and shift for number of quanta
            // slot_bytes = k << 4   右移4位
        slot_bytes = k << SHIFT_NANO_QUANTUM;   
            // multiply by power of two quanta size
        *pKey = k - 1;                  
            // Zero-based!
        return slot_bytes;
    }
    

    malloc库的内存对齐算法几乎跟lib.objc一致

    1. 加15进位
    2. 后15位抹零

    相关文章

      网友评论

          本文标题:002-内存对齐

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