内存管理——申请超过物理内存容量的内存
在4G主存机器申请8G内存会发生什么
- 在 32 位操作系统,因为进程最大只能申请 3 GB 大小的虚拟内存,所以直接申请 8G 内存,会申请失败。
- 在 64 位操作系统,因为进程最大可以申请 128 TB 大小的虚拟内存,即使物理内存只有 4GB,申请 8G 内存也是没问题,因为申请的内存是虚拟内存。如果这块虚拟内存被访问了,要看系统有没有 Swap 分区:
- 如果没有 Swap 分区,因为物理空间不够,进程会被OOM机制杀掉
- 如果有 Swap 分区,即使物理内存只有 4GB,程序也能正常使用 8GB 的内存,进程可以正常运行
问题的关键
通过上述结论可以看出,当malloc申请的内存大小超过机器物理内存容量大小时,结果由两个关键要素决定:
- 虚拟内存空间大小
- swap机制
进程能够访问到的地址范围由虚拟地址空间大小决定,如果申请的内存大小超过这个范围,则会直接申请失败,连虚拟内存都没有分配。
如果申请的内存大小在虚拟内存范围内,则不管物理内存大小剩多少,总是能够申请成功。只不过申请的内存是虚拟内存,只有进程访问这些虚拟内存时才会使用物理内存建立映射。
如果系统没有swap分区,当物理内存使用完后,因为申请的虚拟内存比物理内存大,继续访问后续的虚拟内存时,由于物理内存没有足够的空闲页框可以使用,内核会触发OOM机制将占用内存最大的进程杀掉。
如果系统有swap分区,则当物理内存不足时,根据LRU算法,把最近最少使用的页面暂时换出磁盘里面,腾出物理内存建立新的映射,等到再次访问的时候再从磁盘中换入物理内存。因此当申请的内存比物理内存大得多时,会不断执行页面的换入换出,从而保证了进程的运行,但是相应的会影响性能,是一种时间换空间的机制。
更深层的分析
其实,上面提到的第一个关键要素虚拟内存空间大小,严格来说并不是说申请的内存大小在虚拟内存范围内就能申请成功。
实际上,当申请的内存大于物理内存大小时,能不能申请成功还受到一个内核参数影响——overcommit_memory。
这个参数在虚拟内存的运行参数目录下,可以用
ls /proc/sys/vm
查看virtual memory运行参数,用cat /proc/sys/vm/overcommit_memory
查看参数内容
“申请的内存大于物理内存大小”这个动作称为overcommit!
虚拟内存运行参数overcommit_memory可以设定的值包括:
#define OVERCOMMIT_GUESS 0
#define OVERCOMMIT_ALWAYS 1
#define OVERCOMMIT_NEVER 2
- 如果设定为 OVERCOMMIT_ALWAYS,表示永远允许 overcommit 这个动作。
- 如果设定为 OVERCOMMIT_NEVER ,表示系统不允许 overcommit 这个动作。
- 如果设定为 OVERCOMMIT_GUESS ,表示让内核根据当前的内存情况进行判断。
默认情况下,overcommit_memory的值设定为 OVERCOMMIT_ALWAYS,因此才会有“总是允许申请的内存超过物理内存大小”的结论。
如果是 OVERCOMMIT_NEVER 的话会有一个判断overcommit上限的标准,只要超过上限就申请失败。
另外一个 OVERCOMMIT_GUESS 情况就比较复杂,需要收集内存信息:
- 系统的free page frame
- 用户空间进程读写文件造成的page cache使用的page frame
- 进程间的share memory机制造成的shmem page frame
- swap分区上空闲的page frame
- slab对象的reclaim page
- 能让系统运行需要预留的totalreserve_pages的page frame
- admin_reserve_kbytes的free page
然后对这些内存信息进行计算,最后比对本次申请virtual memory的page数目和当前计算出来的free数据,如果在保留了足够的page frame之后,还有足够的page可以满足本次分配,那么就批准本次vm的分配。
具体可以参考文末引用文章↓
好文推荐:http://www.wowotech.net/memory_management/overcommit.html