为什么有了虚拟内存物理内存还会OOM?
References
https://www.baeldung.com/linux/overcommit-modes
https://www.kernel.org/doc/Documentation/vm/overcommit-accounting
https://zhuanlan.zhihu.com/p/526647732
OOM
很多教科书上说虚拟内存可以随意分配,不受 RAM 的限制,如 xv6 教学操作系统的虚拟内存可以映射到磁盘任意位置,
但实际上 Linux 的虚拟内存除了RAM只能使用磁盘交换区或交换文件,因此即使有了虚拟内存仍然会发生物理内存 OOM
-
虚拟内存OOM
虚拟内存OOM发生在用户申请时,malloc、new 是对虚拟内存空间进行申请,相当于一种申明,而不是分配实际使用的内存,即这段内存还并没有实际存在于 RAM 或 swap 中
如果申请超过一定限度,就会发生虚拟内存OOM,函数接口返回失败错误码,由用户进行错误处理
-
物理内存OOM
物理内存OOM发生在程序运行时,程序在运行的时候触发缺页异常从而需要分配物理内存,如果此时物理内存不足了,则会使用一些内存回收机制,例如换出一些页到交换区,如果最终物理内存仍然不足,就会发生物理内存OOM,进程将被操作系统杀掉
缺页的原因可能是:
- 该段虚拟内存在物理上位于交换区
- 该段虚拟内存根本还没有被初始化
#include <iostream>
#include <unistd.h>
#include <cstring>
int main() {
long long N = 1e10;
std::cout << "虚拟内存申请开始" << std::endl;
char *p = (char *)malloc(N);
if( p == nullptr) {
std::cout << "虚拟内存 OOM" << std::endl;
return 0;
}
std::cout << "虚拟内存申请完成" << std::endl;
// 此时通过 htop 命令可以看到内存占用并没有增长,因此没有发生物理内存的实际分配
usleep(10000000);
std::cout << "物理内存分配开始" << std::endl;
memset(p,'a', N); // 此时通过 htop 命令可以看到内存占用逐渐增长,正在发生物理内存的实际分配
std::cout << "物理内存分配完成" << std::endl;
usleep(10000000);
return 0;
}
从代码可以看出,申请一段虚拟内存,只有在被初始化,如 memset 后,才会在物理内存上真正分配,否则是不占空间的
当物理内存不足时,可以置换一些页到交换区,swap 相当于逻辑上增大了物理内存,使用户通过虚拟内存可以使用大于 RAM 的内存空间,默认策略下的虚拟内存最大为 RAM + swap ,这里也可以称为逻辑上的总物理内存
虚拟内存OOM
OVERCOMMIT 过度承诺是指系统允许分配比物理内存更大的虚拟内存,但当具体访问使用这段内存时,才真正分配物理内存来实现承诺
OVERCOMMIT 发生在 malloc 申请内存时,因为进程一般不会立即使用所有已申请的虚拟内存,因此才能 OVERCOMMIT
Linux也提供了三种策略,通过 /proc/sys/vm/overcommit_memory 文件来选择策略,可以往这个文件里面写入0、1、2来选择不同的策略,这三个值对应的宏是
-
#define OVERCOMMIT_GUESS 0(默认策略)
允许申请的虚拟内存不能超过 RAM + swap 的总和(一种启发式的规则),拒绝不合理的分配申请,是中间策略,可能发生物理内存OOM,因为系统中不止一个进程,但超过 RAM + swap 的虚拟内存申请显然是难以满足的
Heuristic overcommit handling. Obvious overcommits of address space are refused.
-
#define OVERCOMMIT_ALWAYS 1
用户 malloc 申请多少内存就给多少虚拟内存,不管 RAM + swap 是否够用,容易发生物理内存OOM(即 RAM 和 swap 都用尽时)
Always overcommit. Appropriate for some scientific applications.
Classic example is code using sparse arrays and just relying on the virtual memory consisting almost entirely of zero pages.
系统会把全0的虚拟页映射到同一个物理页上,因此可以使用远大于 RAM + swap 的虚拟内存空间
-
#define OVERCOMMIT_NEVER 2
允许申请的虚拟内存被严格限定为 swap + a configurable amount (default is 50%) of physical RAM,尽量保证不发生物理内存OOM
The total address space commit for the system is not permitted to exceed swap + a configurable amount (default is 50%) of physical RAM.
Depending on the amount you use, in most situations this means a process will not be killed while accessing pages but will receive errors on memory allocation as appropriate.