6-1 内存布局

在这一个章节中,主要讲述的内存布局的中的问题。我们知道了内存是分段的式的。系统将内存分成了这几个段:从下到上,从低地址到高地址分别是:保留区域、代码段(.text)、已初始化数据(.data)(全局变量以及静态变量)、未初始化数据(.bss)(全局变量以及静态变量)、堆区(heap)、栈区(stack)(定义的函数或者方法是在此处工作的)、内核区等。其中栈区是方法的运行区,是从高地址到低地址进行扩展,所以栈是向下增长或事向下扩展的、堆区是alloc的对象以及block copy后的对象,都是存放在这里的。堆是从低地址到高地址进行扩展,向上增长的。

具体如下:

  • 栈区(stack):方法运行。
  • 堆区(heap): 通过alloc的创建的对象以及copy后的对象。
  • bss : 未初始化的全局变量以及静态变量。
  • data : 已初始化的全局变量以及静态变量
  • text : 程序代码区。
内存布局.png

内存管理方案

内存管理方案,根据管理的对象不同可分为如下三种:

  • TaggedPointer:小对象NSNumber

  • NONPOINTER_ISA(非指针型的isa):
    对于64架构下的应用程序
    NONPOINTER_ISA 实际上有64位bit位,但是实际中,只有30或者40位,就可以指代当前类对象的地址,为了提高使用率,其余为存储了内存管理方面的信息。

  • 散列表:复杂的结构,其中包含有引用计数表、弱引用表。

NONPOINTER_ISA(非指针型的isa)

arm64架构:

实际上它有64位bit位,

  • 第一位是indexed标记位:(0 表示是指针型的isa , 1 表示 非指针型的isa),

  • 第二位是has_assoc (0 代表没有 ,1 代表有)当前类是否有关联对象,

  • 第三位是has_cxx_dtor (0 代表没有,1代表有) 当前对象是否有使用到C++相关代码。ARC,也可用这个标记位代表有些对象是通过ARC进行内存管理的。

  • shiftcls 当前对象的内存地址:从第4位开始到35位,均表示对象的内存地址

  • magic (36 -- 41)

  • 第42位 weakly_referenced 弱引用,标示了这个对象是否有弱引用指针。

  • 第43位,deallocating 当前是否进行dealloc操作。

  • 第44位has_sidetable_rc 当前对象如果存储的引用计数已经达到上限,那么所挂的has_sidetable_rc(散列表),就会存储引用计数的相关内容

  • 45--63 extra_rc 代表额外的引用计数。

SideTable(散列表)

散列表在内存中使用SideTables()结构实现的。
Side Tables实际中是一个哈希表,我们可以通过一个指针来找到对应的引用计数表或者弱引用表,在哪一个具体的side table表中。Side Tables 包含有多个side table表。不通的系统,数目不同。

**Side Table的结构

包含有:

  • 自旋锁(spinlock_t)
  • 引用计数表(RefcountMap)
  • 弱引用表(weakfy_table_t)

**为什么不是一个Side Table ,而是一个呢?(查找效率的原因)

假如说只有一张表,那就是说,我们在内存中的分配的所有对象的引用计数都在同一张表中。这是,如果我们要修改某个对象的引用计数值。由于不同对象在不同的线程中创建或分配,为了保证数据的访问安全,我们要对数据进行加锁处理。这个时候,就出现了访问效率的问题。使用分离锁可以很好的解决这个问题。

分离锁

所谓的分离锁,就是将内存对象的引用计数表,分成多个部分。比如说分成8个。就说对于在不同表中的对象可以并发访问,从而提高访问效率。如下图:

分离锁.png

怎样实现快速分流?

所谓的快速分流,就是我们通过对象的指针如何快速的定位到它在哪一张 sidetable表中。

Hash表

对象指针,通过Hash函数,计算出的value值(就是sideTables 的index索引)就是,该对象所在hash表的位置。

Hash查找
给定值是对象内存地址,目标值是数组(sideTables )下的索引。为什么使用hash查找?提高查找效率,避免了遍历的过程。由于存储和访问使用的是同一个Hash函数,所以我们可以通过Key,查找相对应地值,这样就避免了数组的遍历查找的过程。
如图所示:

屏幕快照 2019-09-07 下午6.41.26.png

数据结构(散列表)
  1. Spinlock_t 自旋锁
  • 忙等的锁:所谓忙等,就是当前锁已被其他的线程获取,当前线程会不断的探测该锁,是否有被释放,如果释放了,自己第一时间去获取这个锁。
  • 适用于轻量访问。(引用计数)
  1. 信号量:当它获取不到这个锁的时候,它会阻塞休眠,直到锁被释放后,来唤醒当前线程。
  2. RefcountMap引用计数表(Hash表)(Hash查找)
引用计数表
  • size_t 数据结构如图:
size_t 数据结构
weak_table_t 弱引用计数表(Hash 表)

weak_entry_t 结构体数组:存储的是弱引用指针
(__weak,__block)。

屏幕快照 2019-09-07 下午7.06.59.png

你可能感兴趣的:(6-1 内存布局)