美文网首页
内存优化之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是什么?

    大家通常是否会认为isa就是对象的指针,用来表明对象所属的类型。但是如果isa指针仅表示类型的话,对内存显然也是一...

  • OC基础 Runtime-isa (2)

    Runtime-isa 设值 isa之设值:与运算 | Main.m 优化:位域再次优化 使用结构体 再次优化 u...

  • iOS Class实现原理-isa

    本文会阐述下面几个问题 1、isa是什么2、isa的内存布局3、Class与isa背后的设计 查看源码(源码版本o...

  • Runtime

    isa 在ARM 64之前,isa是一个指针,存储着Class、Meta-Class对象的内存地址。ARM 64之...

  • 【android】Android内存优化之OOM

    Android内存优化之OOM Android的内存优化是性能优化中很重要的一部分,而避免OOM又是内存优化中比较...

  • isa的理解及对象的内存地址分布

    栈内存分布是连续的,在创建对象开辟内存的过程中,isa是对象的第一个成员变量。isa到底是什么看看官方解释:Eve...

  • 4-1 内存管理

    1.内存布局 2.iOS内存管理方案 [isa指针保存了内存管理的信息] 2.iOS内存管理方案 [isa指针...

  • Flutter优化相关

    Flutter之禅 内存优化篇Flutter ImageCacheFlutter 性能优化 Tips

  • 腾讯面试

    1.nsarray 为啥用copy 2.深拷贝 浅拷贝 3.通知 有可能阻塞主线程不? 4.内存优化 5.isa

  • isa优化

    & 运算 1>>01>>11>>21>>3 相当于将1 0b 0000 0001,= 1如果左移1为,会变为 0b...

网友评论

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

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