为什么有了虚拟内存物理内存还会OOM?

文章探讨了虚拟内存如何与物理内存交互,解释了虚拟内存OOM和物理内存OOM的区别,以及Linux中的OVERCOMMIT内存管理策略,包括默认的启发式处理、总是分配和严格限制策略。通过代码示例说明了内存分配的时机和过程。
摘要由CSDN通过智能技术生成

为什么有了虚拟内存物理内存还会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.

个人理解,欢迎指正

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值