虚拟内存、物理内存与OOM Killer

文章探讨了在多节点部署中,进程因资源不足而被OOMKiller杀死的问题,重点分析了虚拟内存、物理内存的工作原理以及延迟映射策略。通过测试代码展示了内存分配和映射的过程,指出高内存消耗可能导致OOMKiller干预。最后,提出了一些防止进程被杀的措施,如优化资源需求和配置参数。
摘要由CSDN通过智能技术生成

虚拟内存、物理内存与OOM Killer

问题描述

测试环境中集群部署(各个节点功能相同)部署在同一主机上,启动时无异常,运行一段时间后,每个节点的进程组中的进程会出现异常终止的现象,且异常终止的进程并不完全相同。

问题分析

1.多节点同时部署改为单节点手动部署,每部署一个节点后等待十分钟。
前两个节点成功完成了部署,然而第三个节点的某一进程无法启动导致后面的进程也无法照常启动,但与同时启动多个节点不同的是前两个节点的进程无异常可以正常工作。
2.分析启动失败的进程
该进程的功能为向下游发送数据,因前两个节点的全部进程无异常,问题可能并没有出现在该进程上,再次检查第三个节点进程组的运行状态发现其中曾经成功启动的进程也有几个被kill掉。
3.分析第三节点被kill掉的进程
分析各进程的功能后定位在一个读取参考文件的进程上,因集群不做持久化存储,会将存储网关中的大量数据读入到共享内存,初步分析为系统资源不足导致进程出现异常。
4.开发机复现并使用脚本实时进程监控系统资源
测试结果为物理内存和交换区空间即将全部用尽时部分进程开始出现异常,并释放了大量使用的内存空间,确认该问题为内存资源不足所导致的。

为什么资源成功创建但使用该资源的内存会出现异常呢?

shmget 用于创建或获取一个共享内存标识符,该标识符用于唯一标识一个共享内存区域。这个调用通常在创建共享内存时被使用。shmat 用于将共享内存附加到进程的地址空间,使得进程可以访问共享内存中的数据。这个调用通常在进程需要使用共享内存时被使用。
**这两个调用并不直接涉及立即绑定物理内存。**相反,它们是在虚拟地址空间中创建共享内存的映射。物理内存的分配和映射是在共享内存区域被实际使用时发生的。这种延迟的方式使得系统能够更好地管理内存,只有在需要时才分配物理内存。malloc也同样如此。

虚拟内存和物理内存

**虚拟内存:**是一个抽象概念,虚拟内存使得进程可以拥有一块儿从0开始连续的、独立的内存空间,虚拟内存通过映射技术将虚拟地址映射到物理地址或磁盘的交换空间中。
**物理内存:**物理内存是计算机硬件实际存在的内存。

延迟映射

默认情况下,Linux 采用乐观的内存分配策略。 这意味着,当 malloc() 返回非空时,并不能保证内存真的可用。 万一系统内存不足时,一个或多个进程将被 OOM Killer杀死。

~/Code/mem (main*) » man malloc 
...
NOTES
       By default, Linux follows an optimistic memory allocation strategy.  This means that when malloc() returns non-NULL there is no guarantee that the memory really is available.  In
       case  it  turns  out that the system is out of memory, one or more processes will be killed by the OOM killer.  For more information, see the description of /proc/sys/vm/overcom‐
       mit_memory and /proc/sys/vm/oom_adj in proc(5), and the Linux kernel source file Documentation/vm/overcommit-accounting.rst.
...

测试代码

#include <cstdlib>
#include <cstring>
#include <unistd.h>

using namespace std;

int main(int, char **) {
  constexpr size_t memory_block = 1024 * 1024 * 1024;
	
  // 分配10g虚拟内存空间
  void *p = malloc(10 * memory_block);
  // 访问前1g内存空间
  memset(p, 0, memory_block);
  sleep(1000);
  
  return 0;
}

监测系统资源

使用free -h查看真实使用的内存空间
物理内存和交换区总共使用的内存并没有达到10g,表明没有将申请的10g内存空间全部映射到物理内存中。

~/Code/NetExample/app/bin (main*) » free -h
               total        used        free      shared  buff/cache   available
Mem:           7.7Gi       3.4Gi       3.9Gi       0.0Ki       517Mi       4.2Gi
Swap:          8.0Gi       219Mi       7.8Gi

使用top命令
分配了10g的内存空间,但是只映射了其中使用memset访问的1g的内存

~/Code/NetExample/app/bin (main*) » top
...
761182 ubuntu    20   0   10.0g   1.0g   2.3m S   0.0  12.9   0:00.32 mem
...

OOM Killer

Out-of-Memory (OOM) Killer 是 Linux 内核中的一个机制,用于处理系统内存耗尽的情况。当系统内存不足,没有可用的物理内存和交换空间时,OOM Killer 会尝试终止一个或多个进程,以释放内存,从而防止系统死机或变得不可用。

OOM Killer的策略为杀掉最少进程数尽可能多的释放内存

测试代码

#include <cstdlib>
#include <cstring>
#include <format>
#include <iostream>

using namespace std;

int main(int, char **) {
  constexpr size_t memory_block = 1024 * 1024 * 1024;
  size_t block_size = 0;

  while (true) {
    void *p = malloc(memory_block);
    if (p != nullptr) {
      ++block_size;
    }
    cout << format("malloced {}g virtual memory\n", block_size);

    memset(p, 0, memory_block);
    cout << format("used {}g physical memory\n", block_size);

    system("free -h");
    cout << endl;
  }

  return 0;
}

结果分析

可从虚拟内存映射到物理内存中内存大小为剩余物理内存空间(Mem中的available)和剩余交换空间(Swap中的free)的总和。在进程被kill前虽然成功申请到了虚拟内存空间,但是却因为没有足够的物理空间大小导致OOM Killer开始清理进程。

~/Code/mem (main*) » app/bin/mem
malloced 1g virtual memory
used 1g physical memory
               total        used        free      shared  buff/cache   available
Mem:           7.7Gi       3.3Gi       4.0Gi       0.0Ki       397Mi       4.2Gi
Swap:          8.0Gi       140Mi       7.9Gi

malloced 2g virtual memory
used 2g physical memory
               total        used        free      shared  buff/cache   available
Mem:           7.7Gi       4.3Gi       3.0Gi       0.0Ki       397Mi       3.2Gi
Swap:          8.0Gi       140Mi       7.9Gi

malloced 3g virtual memory
used 3g physical memory
               total        used        free      shared  buff/cache   available
Mem:           7.7Gi       5.3Gi       2.0Gi       0.0Ki       397Mi       2.2Gi
Swap:          8.0Gi       140Mi       7.9Gi

malloced 4g virtual memory
used 4g physical memory
               total        used        free      shared  buff/cache   available
Mem:           7.7Gi       6.3Gi       1.0Gi       0.0Ki       397Mi       1.2Gi
Swap:          8.0Gi       140Mi       7.9Gi

malloced 5g virtual memory
used 5g physical memory
               total        used        free      shared  buff/cache   available
Mem:           7.7Gi       7.3Gi        86Mi       0.0Ki       339Mi       257Mi
Swap:          8.0Gi       140Mi       7.9Gi

malloced 6g virtual memory
used 6g physical memory
               total        used        free      shared  buff/cache   available
Mem:           7.7Gi       7.6Gi        99Mi       0.0Ki        55Mi        37Mi
Swap:          8.0Gi       924Mi       7.1Gi

malloced 7g virtual memory
used 7g physical memory
               total        used        free      shared  buff/cache   available
Mem:           7.7Gi       7.6Gi       133Mi       0.0Ki        58Mi        74Mi
Swap:          8.0Gi       1.9Gi       6.1Gi

malloced 8g virtual memory
used 8g physical memory
               total        used        free      shared  buff/cache   available
Mem:           7.7Gi       7.6Gi        75Mi       0.0Ki        58Mi        16Mi
Swap:          8.0Gi       2.9Gi       5.1Gi

malloced 9g virtual memory
used 9g physical memory
               total        used        free      shared  buff/cache   available
Mem:           7.7Gi       7.6Gi        88Mi       0.0Ki        56Mi        27Mi
Swap:          8.0Gi       3.9Gi       4.1Gi

malloced 10g virtual memory
used 10g physical memory
               total        used        free      shared  buff/cache   available
Mem:           7.7Gi       7.6Gi        66Mi       0.0Ki        60Mi       7.0Mi
Swap:          8.0Gi       4.9Gi       3.1Gi

malloced 11g virtual memory
used 11g physical memory
               total        used        free      shared  buff/cache   available
Mem:           7.7Gi       7.6Gi       118Mi       0.0Ki        63Mi        61Mi
Swap:          8.0Gi       5.9Gi       2.1Gi

malloced 12g virtual memory
used 12g physical memory
               total        used        free      shared  buff/cache   available
Mem:           7.7Gi       7.6Gi        81Mi       0.0Ki        68Mi        26Mi
Swap:          8.0Gi       6.9Gi       1.1Gi

malloced 13g virtual memory
used 13g physical memory
               total        used        free      shared  buff/cache   available
Mem:           7.7Gi       7.6Gi        76Mi       0.0Ki        66Mi        21Mi
Swap:          8.0Gi       7.9Gi       125Mi

malloced 14g virtual memory
[1]    756754 killed     app/bin/mem
------------------------------------

如何防止进程被OOM Killer杀掉

可以通过修改/proc/sys/vm/overcommit_memory或/proc/sys/vm/oom_adj的值来修改进程的分数或禁止OOM Killer。
但是这不是一个完美的解决方案,一个高可用进程更应该从这两点下手:
1.部署在具有充足的硬件资源的机器上
2.做好应急方案,在内存到达80%即将耗尽及时通知并处理

  • 21
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值