虚拟内存和物理内存
计算机组成原理里可知,32位CPU对应32位OS,它的寻址空间是2的32次方,约为4GB,意味着在32位的计算机系统中,理论支持的物理最大寻址空间是4GB。
1 | 32位CPU只有32根地址总线,所以最大的寻址能力是2的32次方 |
虚拟内存到物理内存的映射
主要是两个寄存器起作用,界限寄存器用来判断是否越界,如果没有越界就会加上基址寄存器的值,转换为物理内存地址。
虚拟内存是OS提供给进程使用的概念,而物理内存是CPU提供给OS的概念,从逻辑地址到物理地址最终由CPU中的内存管理单元MMU(Memory Management Unit)进行映射,这样每个进程都能有0x00000000~0xffffffff的寻址空间,物理RAM有限,所有进程分配的内存总量会超过物理RAM数量,有了虚拟内存,OS可以使用硬盘来缓存RAM中无法保存的数据
iOS中没有内存置换的技术,这里讨论的是一般的OS,OSX或者Windows
虚拟内存的意义如下:保护了每个进程的地址空间、简化了内存管理,利用硬盘空间扩展了内存空间
内存分页
为了方便映射和管理,虚拟内存和物理内存都被分割成相同大小的单位,物理内存最小单位被称为帧(Frame),虚拟内存最小单位被称为页(Page)。
物理内存是按照4kb为一帧被划分开,虚拟内存同样按照4kb为一页来划分,每页的字节数与每帧的字节数始终相同,这样便可以将进程中的每页无缝映射到物理RAM中的每帧。
虚拟内存这么划分的好处是,可以将进程中连续的地址空间映射到物理RAM中不连续的地址空间中(支持了物理内存的离散使用),这可以最大限度节省内存碎片的产生。当一个进程启动时,OS会创建一张表,来保存虚拟内存到物理RAM的映射关系,这个表被称为“分页表”,类似windwos中的HANDLE
进程中RAM被写满后怎么办?
在OS X中,会将某些不活跃的帧存到磁盘,但是在iOS中,由于没有内存置换技术,系统会直接将某些不活跃的帧清空!也就是进程在后台被系统杀死了,这主要是由于RAM和磁盘进行数据交换会极大的影响性能。
进程访问的地址找不到怎么办?
如果OS确定进程访问的地址是错误的,则报错,终止进程;如果进程访问的地址被保存到了磁盘上,OS首先分配一帧内存用来保存请求页,如果当前没有可用帧,将现有帧缓存到磁盘,腾出空间,然后将请求读到内存中,更新进程的页表,最后将控制权返回给进程。
Resident Memory是进程的virtual memory中常驻物理RAM中的部分。
脏内存和干净内存
Clean Memory
Clean memory is file-backed memory in which the contents are in sync with disk.
对于一般的桌面系统,clean memory可以认为是能够进行Page Out(将优先级低的内存数据交换到磁盘上的操作)的部分。
在iOS中Clean Memory由于在磁盘中有备份,能够再次读取,所以是可以被重新创建的内存,包括以下几种:
- system framework
- binary executable of your app
- memory mapped files
一般来说,Clean Momory是只读的,clean在memory warning的时候可以被discard掉,然后recreate出来,例如JPEG图片被加载到内存中时,用的是mmap,是clean Memory
Dirty Memory
Dirty memory is file-backed memory in which the contents have been modified but not yet written back to disk. The in-memory version of data is out of sync with the on-disk version and is said to be dirty. A mechanism called writeback will eventually update the on-disk version, bringing the two in sync.
脏内存是磁盘上已经被修改但尚未回写到磁盘的内存:通常包括以下几种:
- Memory containing buffered writes that have not been flushed to disk yet
- Regions of memory mapped files that have been updated but not written out to disk yet.
- Pages that are in the process of being written to swap space but have changed since the system started writing them to swap space.
在iOS中所有不是Clean Memory的内存都是脏内存,系统无法回收。主要包括以下几种:
- Heap allocation
- caches
- decompressed images
当存在内存压力时,系统将卸载一些干净内存,当再次需要改内存时,系统将重新创建它们。但是对于脏内存,系统无法卸载他们,并且iOS没有交换机制,因此脏内存将始终保留在物理内存中,直到达到一定限制为止,然后你的应用被终止,并且所有内存将被回收给系统。
eg:
1 | NSString* str=[NSString stringWIthUTF8String:@"Helllo World"]; |
- 动态分配在heap上的对象,为dirty momory,无法被回收
- 字符串在编译的时候会被存放在代码段中的read-only常量区,是clean的
- UIImage是decommpress出来的data,是dirty memory
既然iOS的脏内存无法被回收,那么我们释放对象的意义是什么?是为了方便脏内存压缩算法么?heap上释放之后的内存是什么内存,会不会被回收?
Private和Shared Memory
RAM中可以被多个进程共享的部分称为Shared Memory,比如系统的Framework,它只映射一份代码到内存,这部分内存会被不同的进程公用,而每个进程单独alloc的内存,则是Pricate Memory
内存之间的关系
虚拟内存就是你的应用程序所需的全部内存
virtual memory = clean memory + dirty memory.
常驻内存是实际加载到物理内存中的内存(物理内存层面),它表示所有脏内存和部分干净内存
resident memory = dirty memory + clean memory that loaded in physical memory
你的程序有时候会因为系统内存不足而被杀死,这个时候应该更多关注物理内存层面
iOS内存管理机制
iOS系统中,App的基本内存管理原则是保证前台App的运行,可能回收后台App(传说中的假后台机制)
iOS中的内存回收管理技术有以下几种:
- 脏内存压缩技术–Compressed Memory
- 内存警告机制–Responding to Memory Warnings
- 低内存回收机制–Jstsam
Compressed Memory
由于内存容量和读写寿命的限制,iOS没有Disk swap机制,取而代之使用Compressed memory。Disk swap是指mac OS以及一些其他桌面系统中,当内存可用资源紧张时,系统将内存中的内容写入磁盘中backing store(swaping out)并且在需要访问时从磁盘中再读入RAM(swaping in)。与大多数UNIX系统不同的是,macOS没有预先分配磁盘中的一部分作为backing store,而是利用引导分区所有可用的磁盘空间。
苹果最初只是公开了从OS X Mavericks开始使用Compressed memory技术,但iOS系统也从iOS7开始悄悄地使用,从OS X Mavericks Core Technology Overview文档中可以了解到该技术在内存紧张时能够将最近使用过的内存占用压缩至原有大小的一半以下,并且能够在需要时解压复用,它在节省内存的同时提高了系统的响应速度,特点如下:
- Shrinks memory usage减少了不活跃内存占用
- Improves power efficiency改善电源效率,通过压缩减少磁盘IO带来的损耗
- Minimizes CPU usage压缩/解压十分迅速,能够尽可能减少CPU的时间开销
- Is multicore aware支持多核操作
内存警报
在当压缩脏内存依然不解决问题,导致内存压力过大则会触发内存警报,可以通过以下四种途径获取警报:
- applicationDidReceviewMemoryWaring协议方法
- [UIViewController didReceiveMemoryWaring]实例方法
- UIApplicaitonDidReceiveMemoryWaringNotification内存警告通知
- DISPATCH_SOURCE_TYPE_MEMORYPRESSURE Dispatch触发源
开发者收到内存警报后可以主动回收程序的内存空间,以达到缓解内存空间使用压力。假如App的占用内存还是控制不住的时候,最终将导致OOM(Out of Memory)崩溃,这个崩溃依赖于OS提供的低内存回收机制–Jetsam
Jetsam
iOS系统中存在一种系统级的内存回收服务(Jetsam)。它是一个常驻系统中的进程,在内核中的线程执行优先级比其他进程的执行优先级都要高,这也是它能够强制回收内存空间的缘故。
低内存处理机制Jetsam,这是一个基于优先级队列的机制,
优先级由低到高是:IDLE(空闲)->BACKGROUND->FOREGROUND,依次类推。当内存过低的时候,就会在队列中进行广播,希望大家尽量释放内存,如果一段时间后,仍然内存不够,就会开始Kill进程,直到内存够用