内存分区
iOS的内存分为:栈区、堆区、全局区/静态区、常量区、代码区
1、栈区
用于存储函数的局部变量、函数参数以及函数调用的上下文信息。
数据在栈上的分配和释放,遵循“后进先出”的原则。
栈的大小通常固定,由操作系统或编译器决定。
栈上的数据生命周期短暂,当函数退出时,其局部变量会自动释放。
注意:栈区的内存是有系统分配并管理的,所以它由系统分配释放,速度快,效率高。
2、堆区
堆区用于存储动态分配的内存,通常通过malloc、calloc、alloc、new等函数来分配。
堆上的数据生命周期较长,需要手动释放(使用free或delete),若不及时释放会造成内存泄漏。
堆的大小通常比栈大,操作系统会提供动态扩展堆的能力,即堆上的内存不足以满足程序的需求时,操作系统会提供动态扩展堆的能力,即在堆空间不够时自动增加堆的大小。
堆上的数据可以在不同函数之间共享。
堆和栈的区别:
生命周期:栈上数据生命周期短,随着函数的退出而自动释放;堆上的数据生命周期长,需要手动释放。分配方式:栈上的内存分配和释放速度快,是自动管理的;堆上的内存需要手动分配和释放。
大小限制:栈的大小通常有限,由系统或编译器决定;堆的大小通常比栈大,可根据需要动态扩展。
栈:由编译器自由分配并释放,速度快,申请的地址是连续的,不会产生内存碎片。优点是快速高效,缺点是大小有限制,数据不灵活。
堆:有程序员分配和释放,速度比较慢,申请的地址不是连续的,容易产生内存碎片,但用起来方面。优点是灵活方便,数据适应面广泛,缺点是效率有一定下降,需要手动管理内存。
3、全局区/静态区
该区是编译时分配的内存空间,用于存储全局变量和静态变量。
这些数据在程序启动时就会分配内存,生命周期和程序时间一致,由系统管理相关内存。
为初始化的全局变量和静态变量,即BSS区(.bss)
已初始化的全局变量和静态变量,即数据区(.data)
4、常量区
该区是编译时分配的内存空间,存储常量数据,如字符串、整型、浮点型常量。
这些数据在程序启动时就会分配内存,生命周期和程序时间一致,由系统管理相关内存。
这些数据通常是只读的,不能修改。
5、代码区
该区是编译时分配的内存空间,存储的是程序运行时被编译成的二进制代码。
这些数据在程序启动时就会分配内存,生命周期和程序时间一致,由系统管理相关内存。
这些数据是只读的
内存管理方案
1、Tagged Pointer
主要用于存储小的、简单的数据对象,如NSNumber、NSDate等。
指针的值不是地址而是真正的值,它的引入不但减少了64位机器下程序的内存占用,还提高的效率,也解决了小内存对象在存储访问上的效率问题。
举例:
存储一个NSNumber对象,其值是一个整数。如果这个整数只是一个NSInteger的普通变量,那么它所占用的内存是与CPU的位数有关,在32位CPU下占4个字节,在64位CPU下是占8个字节的。
而指针类型的大小通常也是与CPU位数相关,一个指针所占用的内存在32位CPU下为4个字节,在64位CPU下也是8个字节。在未使用Tagged Pointer技术之前,NSNumber、NSDate这类的对象所占用的内存会翻倍。
32位CPU
64位- 未使用Tagged Pointer
引入Tagged Pointer对象后,可以将一个对象的指针直接拆成两部分,一部分直接保存数据,另一部分做特殊标记,表示这是一个特别的指针,不指向任何一个地址。
64位- 使用Tagged Pointer
当8字节可以用于表示的数值时,系统就以Tagged Pointer的方式生成指针,若8字节承载不了,又用之前的方式生成普通的指针。
2、NONPOINTER_ISA
先看图:
第0位:indexed,0表示纯isa,1表示isa加内存管理
第1位:has_assoc,当前对象是否有关联对象
第2位:has_cxx_dtor,当前对象是否有c++相关的使用
第3-35位:shiftcls,当前对象的类对象指针地址
第31-41位:magic,不了解
第42位:weakly_refrenced,当前对象是否有弱引用指针
第43位:deallocating,当前对象是否进行dealloc操作
第44位:has_sidetable_rc,当前对象isa引用计数到上限,是否需要外带散列表
第45-63位:extra_rc,额外的引用计数,当引用计数较少的时候,在这些位上存储,多的时候44位为1,外接散列表。
3、散列表
为了管理所有对象的引用计数和weak指针,系统创建了全局的SideTable,它实际是一个全局的hash表。里面存储的是SideTable结构体,使用对象的内存(对象指针)地址作为key。
struct SideTable {
spinlock_t slock; //锁
RefcountMap refcnts;//引用计数表
weak_table_t weak_table;//弱引用表
SideTable() {
memset(&weak_table, 0, sizeof(weak_table));
}
~SideTable() {
_objc_fatal("Do not delete SideTable.");
}
void lock() { slock.lock(); }
void unlock() { slock.unlock(); }
void reset() { slock.reset(); }
// Address-ordered lock discipline for a pair of side tables.
template<HaveOld, HaveNew>
static void lockTwo(SideTable *lock1, SideTable *lock2);
template<HaveOld, HaveNew>
static void unlockTwo(SideTable *lock1, SideTable *lock2);
};