内存分区
堆(heap):使用malloc,realloc,he free函数控制的变量,堆在所有的线程,共享库,和动态加载的模块中被共享使用,存储对象的属性,方法定义。
栈(stack):函数调用是使用栈来保存函数现场,自动变量(即生命周期限制在某个scope的变量)
.data:初始化的了全局变量和静态变量
.bss: 未初始化的全局变量和静态变量
内存管理技术
移动端的内存管理技术,主要有 GC(Garbage Collection,垃圾回收)的标记清除算法和苹果公司使用的引用计数方法。
Objective-C中提供了两种内存管理机制:MRC(MannulReference Counting)和 ARC(Automatic Reference Counting)
MRC是手动管理引用计数:
在MRC的内存管理模式下,与对变量的管理相关的方法有:retain, release 和 autorelease。retain 和 release 方法操作的是引用记数,当引用记数为零时,便自动释放内存。
ARC是自动内存管理机制:会根据引用计数自动监视对象的生存周期,实现方式是在编译时期自动在已有代码中插入合适的内存管理代码以及在 Runtime 做一些优化。
ARC下,属性声明关键字以及对内存的影响
@property (assign/retain/strong/weak/unsafe_unretained/copy) Number* num
assign表明 setter 仅仅是一个简单的赋值操作,通常用于基本的数值类型,例如CGFloat和NSInteger
strong:强引用,声明为强引用可以避免此指针指向的内存被释放,在ARC的规则下,当一块内存没有强指针指向它的时候,它就会被释放。
weak:弱引用,如果弱引用指向的对象没有其他强指针指向它,那么此内存会被释放,此引用会被至为nil
unsafe_unretained:跟弱指针一样,但是不会被至为nil,会成为一个野指针
__autoreleasing 用于标示使用引用传值的参数(id *),在函数返回时会被自动释放掉。
引用循环:
当两个对象互相持有对应的强引用,就会造成引用循环,
想要破除引用循环:
需要把一方设置为weak,弱引用,
其他方法:
当示例变量完成工作后,将其至为nil
Autorelease Pool
Autorelase Pool 提供了一种可以允许你向一个对象延迟发送release消息的机制。当你想放弃一个对象的所有权,同时又不希望这个对象立即被释放掉(例如在一个方法中返回一个对象时),Autorelease Pool 的作用就显现出来了。
所谓的延迟发送release消息指的是,当我们把一个对象标记为autorelease时:
NSString* str = [[[NSString alloc] initWithString:@"hello"] autorelease];
这个对象的 retainCount 会+1,但是并不会发生 release。当这段语句所处的 autoreleasepool 进行 drain 操作时,所有标记了 autorelease 的对象的 retainCount 会被 -1。即 release 消息的发送被延迟到 pool 释放的时候了。
在 ARC 环境下,苹果引入了 @autoreleasepool 语法,不再需要手动调用 autorelease 和 drain 等方法。
在 ARC 下,我们并不需要手动调用 autorelease 有关的方法,甚至可以完全不知道 autorelease 的存在,就可以正确管理好内存。因为 Cocoa Touch 的 Runloop 中,每个 runloop circle 中系统都自动加入了 Autorelease Pool 的创建和释放。
内存的应用与实战技巧
iOS中,如果一个应用程序占用了过高的内存,就会被系统杀死,所以在实际的代码开发中,应该注意内存的占有,注意有没有内存泄漏。
对于在 iOS 开发过程中如何优化内存,苹果公司在 2018 年的 WWDC Session 416: iOS Memory Deep Dive上进行了详细讲解
检查一个应用程序有没有泄漏,
1.xcode可以运行起来,看看他的内存有没有随着时间一直在增长,如果随着时间一直在递增,说明有内存泄漏。
2.用xcode自带的工具leaks,检测有没有内存泄漏,如果有,那么通过图中红圈的几个选择,可以查看具体泄漏的方法。
内存延伸知识:冯*诺依曼体系
在冯*诺依曼体系结构中:
一个计算机应该包含哪些模块:
一:CPU
1.包含算术逻辑单元(Arithmetic Logic Unit,ALU)和处理器寄存器(Processor Register)的处理器单元(Processing Unit),用来完成各种算术和逻辑运算。因为它能够完成各种数据的处理或者计算工作,因此也有人把这个叫作数据通路(Datapath)或者运算器。
2.一个包含指令寄存器(Instruction Register)和程序计数器(Program Counter)的控制器单元(Control Unit/CU),用来控制程序的流程,通常就是不同条件下的分支和跳转。
在现在的计算机里,上面的算术逻辑单元和这里的控制器单元,共同组成了我们说的 CPU。
二:内存:
三:输入输出设备
所有的计算机程序,也都可以抽象为从输入设备读取输入信息,通过运算器和控制器来执行存储在存储器里的程序,最终把结果输出到输出设备中。而我们所有撰写的无论高级还是低级语言的程序,也都是基于这样一个抽象框架来进行运作的。
内存延伸知识:虚拟内存和物理内存
说到内存管理的演进过程,在最开始的时候,程序是直接访问物理内存,但后来有了多程序多任务同时运行,就出现了很多问题。比如,同时运行的程序占用的总内存必须要小于实际物理内存大小。再比如,程序能够直接访问和修改物理内存,也就能够直接访问和修改其他程序所使用的物理内存,程序运行时的安全就无法保障。
虚拟内存
由于要解决多程序多任务同时运行的这些问题,所以增加了一个中间层来间接访问物理内存,这个中间层就是虚拟内存。虚拟内存通过映射,可以将虚拟地址转化成物理地址。
App 在运行时,大多数的时间只会使用很小部分的内存,所以我们可以使用比段粒度更小的空间管理技术,也就是分页。
分页就是把地址空间切分成固定大小的单元,这样我们就不用去考虑堆和栈会具体申请多少空间,而只要考虑需要多少页就可以了。这,对于操作系统管理来说也会简单很多,只需要维护一份页表(Page Table)来记录虚拟页(Virtual Page)和物理页(Physical Page)的关系即可。