站在巨人的肩膀上展望未来!
本文章引用了如下文章中的内容:
https://www.cnblogs.com/wanghuaijun/p/6509016.html
https://www.cnblogs.com/guochaoxxl/p/6977773.html
https://blog.csdn.net/u012400600/article/details/51589382
https://www.cnblogs.com/wendingding/p/3704739.html
iOS是以OC为基础语言,OC又是建在C的基础之上的一个面向对象的语言,所以看iOS的内存管理,需要先从C入手!
1.0 C程序结构
c程序结构是分为两个时期的:储存时、运行时!
储存时(并没有调入内存):代码区(text)、数据区(data)、未初始化数据区(bss)
代码区:存放CPU执行的机器指令,通常只读(就是函数)。
数据区:全局初始化数据区/静态数据区,包括:程序中明确被初始化的全局变量、静态变量(全局静态和局部静态变量)、常量数据(字符串常量)。
未初始化数据区:存放全局未初始化变量,在程序执行之前内核初始化为0或者空指针NULL。
运行时(在储存时的三块又增加两块):栈(stack)、堆(heap)
一个经典分析.png栈:由编译器自动分配释放,存放着函数的参数值、局部变量值、返回值等,并且是连续储存、占用空间小、向低地址扩展。
堆:用来动态内存分配,程序员通过特殊的函数命令进行创建(C 中malloc、OC中的alloc,new等),并且非连续储存、占用空间大,相高地址扩展。
2.0内存分配方式
两种说法:静态分配、动态分配 或者 从静态储存区分配、栈分配、堆分配。
(1)从静态储存区分配(静态分配):内存在程序编译时就已经分配好,这块内存在程序的整个运行期间都存在。速度快、不容易出错,因为有系统会善后。例如全局变量,static变量等
(2)栈分配(静态分配):在执行函数时,函数内局部变量的存储单元都在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
(3)堆分配(动态分配):程序员通过函数调用创建与释放。
区别:
静态对象是通过名字直接进行操作的
动态对象是虽然是通过名字操作,但实质是操作存在名字里面的内存地址间接操作的
int a = 100;//这里直接进行操作的a
Person *p = [Person new];//这里就是通过new动态分配一个Person对象,并把这个Person对象的内存地址放在p中,所以操作p的实质就是操作p的内存地址
3.0 栈和堆的区别
管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
空间大小:一般来讲在 32 位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是1M(好像是,记不清楚了)。当然,我们可以修改:打开工程,依次操作菜单如下:Project->Setting->Link,在 Category 中选中 Output,然后在 Reserve 中设定堆栈的最大值和 commit。注意:reserve 最小值为 4Byte;commit 是保留在虚拟内存的页文件里面,它设置的较大会使栈开辟较大的值,可能增加内存的开销和启动时间。
碎片问题:对于堆来讲,频繁的 new/delete 势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出,详细的可以参考数据结构,这里我们就不再一一讨论了。
生长方向:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。
分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由 malloc 函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是 C/C++ 函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。
OC对象
OK,到这里我们简单的介绍完关于C的内存管理相关知识,那么我们回过来聊聊OC吧!
在OC中我们关心的内存管理大部分来源于堆内存!而对象就是在堆内存中,换言之,我们大部分就是管理对象的内存!
1.0计数器:
概念:一个表示对象被引用次数的整数值!每个OC对象内部都会特意开辟出四个字节进行储存!
作用:判断是否被回收的唯一依据,不为0存在。
操作:就是通过消息进行操作。retain消息:计数器+1,release消息:计数器-1,retainCount消息:获取当前对象的计数器值。
2.0原则:
只要有人用对象就不能被回收!
谁创建谁release:若通过alloc、new、copy创建的对象,必须调用release或者autorelease。
谁retain谁release:只要调用了retain,无论这个对象何时创建,都需要调用release。
网友评论