美文网首页
内存优化之ISA是什么?

内存优化之ISA是什么?

作者: 大王叫我来巡山丨 | 来源:发表于2021-06-14 10:11 被阅读0次

    大家通常是否会认为isa就是对象的指针,用来表明对象所属的类型。
    但是如果isa指针仅表示类型的话,对内存显然也是一个极大的浪费。于是,就像tagged pointer一样,对于isa指针,苹果同样进行了优化。使得isa指针表示的内容变得更为丰富,除了表明对象属于哪个类之外,还附加了引用计数extra_rc,是否有被weak引用标志位weakly_referenced,是否有附加对象标志位has_assoc等信息。接下来我们一起来看看它的真面目!

    源码解析

    首先,我们回顾一下isa指针是怎么在一个对象中存储的。如下图:


    isa存储.png

    从图中可以看出,我们所谓的isa指针,最后实际上落脚于isa_t的联合体;

    isa_t分析

    union isa_t {
        // 两个构造函数
        isa_t() { }
        isa_t(uintptr_t value) : bits(value) { }
    
        Class cls;
        // bits 可以当做为向外提供了操作struct的对象
        uintptr_t bits;
    #if defined(ISA_BITFIELD)
        struct {
            ISA_BITFIELD;  // defined in isa.h
        };
    #endif
    };
    

    从源码可以看出 isa_t是联合体,isa_t 中包含有cls,bits, struct三个变量,它们的内存空间是重叠的。在实际使用时,仅能够使用它们中的一种,你把它当做cls,就不能当bits访问,你把它当bits,就不能用cls来访问!(问:为什么用联合体呢,它的作用是什么?答:联合体的作用是在于,用更少的空间来表示了更多的类型,但是这些类型是不能够共存的!)
    对联合体有兴趣的小伙伴空闲时间,可以去了解一下,但不建议用于项目中,装逼请随意!敲敲我这三米长的开山刀,请将注意力集中在isa_t 联合上:

    • 有两个构造函数isa_t(), isa_t(uintptr_value)
    • 有三个数据成员Class cls, uintptr_t bits, struct ,其中uintptr_t占据64位内存!

    这里需要注意下,虽说是三个成员,uintptr_t bits 和 struct 其实是一个成员,它们都占据64位内存空间!因为联合体类型的成员内存空间是重叠的。由于uintptr_t bits 和 struct 都是占据64位内存,因此它们的内存空间是完全重叠的!(简单的来说,uintptr_t bits 可以当做为向外提供了操作struct的对象,而struct 本身则说明了uintptr_t bits 中各个二进制位的定义)

    理解了uintptr_t bits 和 struct 关系后,则isa_t其实可以看做有两个可能的取值,Class cls或struct。如下图所示:


    isa_t.png

    当isa_t作为Class cls使用时,这符合了我们之前一贯的认知:isa是一个指向对象所属Class类型的指针。然而,让一个64位的指针表示一个类型,是不是有些不划算呢。因此,绝大多数情况下,苹果采用了优化的isa策略,isa_t 类型并不等同于Class cls,而是struct!

    一起来看一下struct的结构 :

    # if __arm64__
    #   define ISA_MASK        0x0000000ffffffff8ULL
    #   define ISA_MAGIC_MASK  0x000003f000000001ULL
    #   define ISA_MAGIC_VALUE 0x000001a000000001ULL
    #   define ISA_BITFIELD                                                      \
          uintptr_t nonpointer        : 1;                                       \  注意:区分isa_t是否是一个真正的指针!!!
          uintptr_t has_assoc         : 1;                                       \
          uintptr_t has_cxx_dtor      : 1;                                       \
          uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
          uintptr_t magic             : 6;                                       \
          uintptr_t weakly_referenced : 1;                                       \
          uintptr_t deallocating      : 1;                                       \
          uintptr_t has_sidetable_rc  : 1;                                       \
          uintptr_t extra_rc          : 19
    #   define RC_ONE   (1ULL<<45)
    #   define RC_HALF  (1ULL<<18)
    
    # elif __x86_64__
    #   define ISA_MASK        0x00007ffffffffff8ULL
    #   define ISA_MAGIC_MASK  0x001f800000000001ULL
    #   define ISA_MAGIC_VALUE 0x001d800000000001ULL
    #   define ISA_BITFIELD                                                        \
          uintptr_t nonpointer        : 1;                                         \
          uintptr_t has_assoc         : 1;                                         \
          uintptr_t has_cxx_dtor      : 1;                                         \
          uintptr_t shiftcls          : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
          uintptr_t magic             : 6;                                         \
          uintptr_t weakly_referenced : 1;                                         \
          uintptr_t deallocating      : 1;                                         \
          uintptr_t has_sidetable_rc  : 1;                                         \
          uintptr_t extra_rc          : 8
    #   define RC_ONE   (1ULL<<56)
    #   define RC_HALF  (1ULL<<7)
    
    # else
    #   error unknown architecture for packed isa
    # endif
    
    // SUPPORT_PACKED_ISA
    #endif
    

    struct共占用64位,从低位到高位依次是nonpointer到extra_rc。成员后面的:表明了该成员占用几个bit。成员的含义如下:


    struct 结构表.png

    由上表可以看出,和对象引用计数相关的有两个成员:extra_rc和has_sidetable_rc。真机所采用19位的extra_rc来记录对象的引用次数,当extra_rc 不够用时,还会借助sidetable来存储计数值,这时,has_sidetable_rc会被标志为1。我们可以算一下,对于19位的extra_rc ,其数值可以表示2^19 - 1 = 524287。 52万多,相信绝大多数情况下,都够用了。

    实践验证

    创建一个对象,并打印isa_t的值

    PeopleModel *model = [[PeopleModel alloc] init];
    NSLog(@"isa_t = %p", *(void **)(__bridge void*)model); // isa_t = 0x1a10286cf39
    

    复制该地址粘贴到计算机中:


    计算器.png

    标红区域请于上图中struct结构表结合查看!当开启了isa_t优化,nonpointer 置位为1, 这时,isa_t *其实不是一个地址,而是一个有意义的值,也就是说,苹果用isa_t * 所占用的64位空间,表示了一个有意义的值,而这64位值的定义,就符合我们上面struct的定义。

    相关文章

      网友评论

          本文标题:内存优化之ISA是什么?

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