1.1 操作系统内存管理路线图
引发的思考:
- 有哪些特殊的场景,需要MRC自己管理内存计数?
- 怎么监控项目循环引用?
三方检测工具MLeaksFinder、OOMDetecter等
1.2 描述
- 系统层级
为什么会出现内存OOM,App被杀死的原因。
多程序多任务执行需要,物理内存不够 => 虚拟内存 => 空间分配更加灵活 => 分段、分页机制 => 内存映射效率的需要 => (DRAM/Jetsam机制)
- App进程层级
内存布局:代码段、数据段、堆、栈。
变量在堆区动态申请、释放内存空间。=> (1. 申请空间不释放 2. 频繁的动态申请,产生内存碎片,不能及时回收)
以一个自定义的Person类为例,分析其内存布局:
堆内存:
Person类的对象本身会在堆内存中分配,存放对象的元信息,如isa指针、对象地址等。
堆内存中的Person对象存储了指向数据区的指针。
数据区:
Person类中的name、age等属性需要存储数据内容,所以会在数据区单独申请内存块。
数据区中将包含Person对象的name、age等属性内容。
Person对象中的指针指向该数据区。
数据区大小可根据需要动态扩展。
代码访问:
创建Person对象时,会同时在堆区与数据区各自分配内存。
通过Person对象的指针访问name、age等属性,实际是访问数据区。
Person对象本身存储在堆区,属性实际内容存储在数据区。
释放:
释放Person对象时,堆区与数据区相关的内存都会被回收。
引用计数机制:创建变量后,会在App进程对应的内存空间堆区,申请一段内存空间,同时对应一个标识位记录变量引用的次数,如果被变量引用后,则+1,引用的变量释放后,则-1,引用次数为0的话,会释放对于的内存空间。
循环引用问题,A->B, B->C, C->A 导致对应内存空间无法释放,需要拆循环!!!
为了使用方便,引入了weak指针,
- weak实现原理
A. 引用计数不会加1
B. objc释放以后自动置为nil
weak_table_t哈希表
1.3 OOM崩溃的治理
- 阀值和时机
获取方式,Jetsam机制,App内存使用阀值为多少,通过系统日志,看需要pageSize * rpages 等于1.4G ;XNU系统entity中获取优先级和内存限制;task_info获取(app内可获取)
怎么触发的呢?
iOS系统启动后,会开启一个vm_pressure_monitor的线程查看系统内存压力;同时通过一个堆栈维护所有的App进程,内存快照表记录各App的内存使用情况。
监控到内存紧张时,就会通知App代理,didReceiveMemoryWarning,给App处理内存释放,否则杀死App进程。
怎么优先级判断呢?
内核>系统进程>前台App>后台App
CPU占用少的>占用多的
App杀死前,至少有6s的实践做优先级判读,并生成JetsamEvent日志。
- 定位申请者
malloc分配内存时,都会走malloc_logger,所以可以使用fishhook去hook函数掌握内存分配情况。
1.4 内存使用建议
官方使用建议:
iOS开发如何优化内存,iOS Memory Deep Dive - WWDC18 - Videos - Apple Developer
WWDC Session: 图片使用最佳实践
App 低内存状态的处理,No pressure, Mon! Handling low memory conditions in iOS and Mavericks
第三方内存监测工具原理解析