在这一个章节中,主要讲述的内存布局的中的问题。我们知道了内存是分段的式的。系统将内存分成了这几个段:从下到上,从低地址到高地址分别是:保留区域、代码段(.text)、已初始化数据(.data)(全局变量以及静态变量)、未初始化数据(.bss)(全局变量以及静态变量)、堆区(heap)、栈区(stack)(定义的函数或者方法是在此处工作的)、内核区等。其中栈区是方法的运行区,是从高地址到低地址进行扩展,所以栈是向下增长或事向下扩展的、堆区是alloc的对象以及block copy后的对象,都是存放在这里的。堆是从低地址到高地址进行扩展,向上增长的。
具体如下:
- 栈区(stack):方法运行。
- 堆区(heap): 通过alloc的创建的对象以及copy后的对象。
- bss : 未初始化的全局变量以及静态变量。
- data : 已初始化的全局变量以及静态变量
- text : 程序代码区。
![](https://img.haomeiwen.com/i13647581/bb135f1a99bd295d.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个。就说对于在不同表中的对象可以并发访问,从而提高访问效率。如下图:
![](https://img.haomeiwen.com/i13647581/81c4e32c61ac9026.png)
怎样实现快速分流?
所谓的快速分流,就是我们通过对象的指针如何快速的定位到它在哪一张 sidetable表中。
Hash表
对象指针,通过Hash函数,计算出的value值(就是sideTables 的index索引)就是,该对象所在hash表的位置。
Hash查找
给定值是对象内存地址,目标值是数组(sideTables )下的索引。为什么使用hash查找?提高查找效率,避免了遍历的过程。由于存储和访问使用的是同一个Hash函数,所以我们可以通过Key,查找相对应地值,这样就避免了数组的遍历查找的过程。
如图所示:
![](https://img.haomeiwen.com/i13647581/945f32bc1d0b2cd4.png)
数据结构(散列表)
- Spinlock_t 自旋锁
- 忙等的锁:所谓忙等,就是当前锁已被其他的线程获取,当前线程会不断的探测该锁,是否有被释放,如果释放了,自己第一时间去获取这个锁。
- 适用于轻量访问。(引用计数)
- 信号量:当它获取不到这个锁的时候,它会阻塞休眠,直到锁被释放后,来唤醒当前线程。
- RefcountMap引用计数表(Hash表)(Hash查找)
![](https://img.haomeiwen.com/i13647581/c881e5417e8af114.png)
- size_t 数据结构如图:
![](https://img.haomeiwen.com/i13647581/4b6bd984fcd70339.png)
weak_table_t 弱引用计数表(Hash 表)
weak_entry_t 结构体数组:存储的是弱引用指针
(__weak,__block)。
![](https://img.haomeiwen.com/i13647581/82febb1240e9d108.png)
网友评论