OOM Killer(Out of Memory Killer)是Linux内核在系统内存严重不足时,强行释放进程内存的一种机制。本文介绍Alibaba Cloud Linux操作系统出现OOM Killer的可能原因,并提供多种解决方案。

问题现象

Alibaba Cloud Linux操作系统在实例全局内存或实例内cgroup的内存不足时,会先触发内存回收机制释放内存,并将这部分被释放的内存分配给其他进程。如果内存回收机制不能处理系统内存不足的情况,则系统会触发OOM Killer强制释放进程占用的内存。触发OOM Killer的部分日志信息示例如下(日志的路径:centos是/var/log/messages,Ubuntu是/var/log/syslog):

  1. 565 [六 9 11 12:24:42 2021] test invoked oom-killer: gfp_mask=0x62****(GFP_HIGHUSER_MOVABLE|__GFP_ZERO), nodemask=(null), order=0, oom_score_adj=0
  2. 566 [六 9 11 12:24:42 2021] test cpuset=/ mems_allowed=0
  3. 567 [六 9 11 12:24:42 2021] CPU: 1 PID: 29748 Comm: test Kdump: loaded Not tainted 4.19.91-24.1.al7.x86_64 #1
  4. 568 [六 9 11 12:24:42 2021] Hardware name: Alibaba Cloud Alibaba Cloud ECS, BIOS e62**** 04/01/2014

可能原因

系统出现OOM Killer表示内存不足,内存不足可以分为实例全局内存不足和实例内cgroup的内存不足。目前常见的出现OOM Killer的原因有以下几种:

原因类型 场景示例
cgroup内存使用达到上限 如下日志记录的出现OOM Killer场景示例中,进程test所在的cgroup /mm_test发生了OOM Killer。 ```

[Wed Sep 8 18:01:32 2021] test invoked oom-killer: gfp_mask=0x240**(GFP_KERNEL), nodemask=0, order=0, oom_score_adj=0 [Wed Sep 8 18:01:32 2021] Task in /mm_test killed as a result of limit of /mm_test [Wed Sep 8 18:01:32 2021] memory: usage 204800kB, limit 204800kB, failcnt 26

 原因:cgroup /mm_test的内存使用率达到上限(200 MB)。  |
| 父cgroup内存使用达到上限 | 如下日志记录的出现OOM Killer场景示例中,进程test属于cgroup /mm_test/2,而发生OOM Killer的cgroup为/mm_test。

[Fri Sep 10 16:15:14 2021] test invoked oom-killer: gfp_mask=0x240**(GFP_KERNEL), nodemask=0, order=0, oom_score_adj=0 [Fri Sep 10 16:15:14 2021] Task in /mm_test/2 killed as a result of limit of /mm_test [Fri Sep 10 16:15:14 2021] memory: usage 204800kB, limit 204800kB, failcnt 1607

 原因:cgroup /mm_test/2的内存使用率没有达到上限,但父cgroup /mm_test的内存使用率达到上限(200 MB)。  |
| 系统全局内存的使用率过高 | 如下日志记录的出现OOM Killer场景示例中,limit of host表示实例的全局内存出现了不足。在日志记录的数据中,空闲内存(free)已经低于了内存最低水位线(low)。

[六 9月 11 12:24:42 2021] test invoked oom-killer: gfp_mask=0x62**(GFP_HIGHUSER_MOVABLE|__GFP_ZERO), nodemask=(null), order=0, [六 9月 11 12:24:42 2021] Task in /user.slice killed as a result of limit of host [六 9月 11 12:24:42 2021] Node 0 DMA32 free:155160kB min:152412kB low:190512kB high:228612kB [六 9月 11 12:24:42 2021] Node 0 Normal free:46592kB min:46712kB low:58388kB high:70064kB

 原因:由于实例的空闲内存低于内存最低水位线,无法通过内存回收机制解决内存不足的问题,因此触发了OOM Killer。 |
| 内存节点(Node)的内存不足 | 如下日志记录的出现OOM Killer场景示例中,部分日志记录说明: <br />- limit of host表示内存节点的内存出现了不足。 <br />- 实例存在Node 0和Node 1两个内存节点。<br />- 内存节点Node 1的空闲内存(free)低于内存最低水位线(low)。 <br />- 实例的空闲内存还有大量剩余(free:4111496)。 <br />

[Sat Sep 11 09:46:24 2021] main invoked oom-killer: gfp_mask=0x62**(GFP_HIGHUSER_MOVABLE|__GFP_ZERO), nodemask=(null), order=0, oom_score_adj=0 [Sat Sep 11 09:46:24 2021] main cpuset=mm_cpuset mems_allowed=1 [Sat Sep 11 09:46:24 2021] Task in / killed as a result of limit of host [Sat Sep 11 09:46:24 2021] Mem-Info: [Sat Sep 11 09:46:24 2021] active_anon:172 inactive_anon:4518735 isolated_anon: free:4111496 free_pcp:1 free_cma:0 [Sat Sep 11 09:46:24 2021] Node 1 Normal free:43636kB min:45148kB low:441424kB high:837700kB [Sat Sep 11 09:46:24 2021] Node 1 Normal: 8564kB (UME) 3758kB (UME) 18316kB (UME) 18432kB (UME) 8764kB (ME) 45128kB (UME) 16256kB (UME) 5512kB (UE) 141024kB (UME) 0 2048kB 0*4096kB = 47560kB [Sat Sep 11 09:46:24 2021] Node 0 hugepages_total=360 hugepages_free=360 hugepages_surp=0 hugepages_size=1048576kB [Sat Sep 11 09:46:24 2021] Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=2048kB [Sat Sep 11 09:46:24 2021] Node 1 hugepages_total=360 hugepages_free=360 hugepages_surp=0 hugepages_size=1048576kB [Sat Sep 11 09:46:25 2021] Node 1 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=2048kB

 原因:在NUMA存储模式下,操作系统可能存在多个内存节点(可运行cat /proc/buddyinfo命令查看相关资源信息)。如果通过cpuset.mems参数指定cgroup只能使用特定内存节点的内存,则可能导致实例在具备充足的空闲内存的情况下,仍出现OOM Killer的情况。  |
| 内存碎片化时伙伴系统内存不足 | 如下日志记录的出现OOM Killer场景示例中,部分日志记录分析说明: <br />- 操作系统在内存分配的order=3阶段出现了OOM Killer。 <br />- 内存节点Node 0的空闲内存(free)仍高于内存最低水位线(low)。 <br />- 内存节点Node 0对应的伙伴系统内存为0(0*32kB (M))。 <br />

[六 9月 11 15:22:46 2021] insmod invoked oom-killer: gfp_mask=0x60**(GFP_KERNEL), nodemask=(null), order=3, oom_score_adj=0 [六 9月 11 15:22:46 2021] insmod cpuset=/ mems_allowed=0 [六 9月 11 15:22:46 2021] Task in /user.slice killed as a result of limit of host [六 9月 11 15:22:46 2021] Node 0 Normal free:23500kB min:15892kB low:19864kB high:23836kB active_anon:308kB inactive_anon:194492kB active_file:384kB inactive_file:420kB unevi ctable:0kB writepending:464kB present:917504kB managed:852784kB mlocked:0kB kernel_stack:2928kB pagetables:9188kB bounce:0kB [六 9月 11 15:22:46 2021] Node 0 Normal: 13254kB (UME) 9668kB (UME) 67516kB (UME) 032kB (M) 064kB 0128kB 0256kB 0512kB 01024kB 02048kB 0*4096kB =

 原因:操作系统的内存在进行内存分配的过程中,如果伙伴系统的内存不足,则系统会通过OOM Killer释放内存,并将内存提供至伙伴系统。 |

<a name="ofmkf"></a>
## 解决方案
阿里云提供了以下解决方案,您可以结合实际情况,排查并解决问题:

| 方案序号 | 方案说明 |
| --- | --- |
| 方案一 | 建议您自行评估实例内当前占用内存的进程情况,及时清理不需要的进程,以释放内存。如果您的业务所需的内存较大,当前实例规格不满足您对内存的需求,可以升配实例以提升实例的内存容量。更多信息,请参见[升降配方式概述](https://help.aliyun.com/document_detail/25437.htm#concept-anb-bbf-5db)。在升配实例后,您还需要根据内存实际的提升情况,手动调整cgroup的内存上限。 <br />调整cgroup内存上限的命令说明如下: <br />`echo value > /sys/fs/cgroup/memory/test/memory.limit_in_bytes` |
| 方案二 | 检查实例内是否存在内存泄漏的情况。重点检查项: <br />- 查看slab_unreclaimable内存使用情况。 <br />
`cat /proc/meminfo &#124; grep "SUnreclaim"`<br />slab_unreclaimable内存为系统不可回收的内存,当占用总内存的10%以上时,表示系统可能存在slab内存泄漏。如果存在内存泄漏问题,您可以手动排查,具体操作,请参见[如何排查Linux slab_unreclaimable内存占用高的原因?](https://help.aliyun.com/document_detail/316787.htm#task-2118313)。<br />- 查看systemd内存使用情况。<br />
`cat /proc/1/status &#124; grep "RssAnon"`<br /> 内核发生OOM Killer时,会跳过系统的1号进程。此时您查看systemd内存使用情况时,一般不会超过200 MB。如果出现异常,您可以尝试自行更新systemd工具的版本。<br />- 查看透明大页THP的性能。 <br />
开启THP会出现内存膨胀(memory bloating),从而导致OOM Killer的出现。您可以对THP进行调优。具体操作,请参见[Alibaba Cloud Linux 2系统中与透明大页THP相关的性能调优方法](https://help.aliyun.com/knowledge_detail/161963.html)。  |
| 方案三 | 内存节点(Node)的内存不足导致的OOM Killer,您需要重新配置cpuset.mems接口的值,使cgroup能够合理使用内存节点的内存。配置cpuset.mems的命令说明如下: <br />1. 运行以下命令,确定系统中内存节点(Node)的数量信息。<br />
`cat /proc/buddyinfo`<br />2. 配置cpuset.mems。 <br />
`echo value > /sys/fs/cgroup/cpuset/test/cpuset.mems `<br />其中,value为对应的内存节点号。 例如,系统中存在三个Node,分别为Node 0、Node 1、Node 2。您需要让cgroup使用Node 0和Node 2两个节点的内存。则value取值为0,2。  |
| 方案四 | 内存碎片化时导致的OOM Killer,建议您定期在业务空闲时间段,进行内存整理。开启内存整理功能的命令为: <br />`echo 1 > /proc/sys/vm/compact_memory` |

<a name="dBUzv"></a>
## oom-killer杀死进程是有逻辑依据的,不是胡乱kill
kill process的顺序是根据/proc/pid/oom-score的值来确定的,值越大被干掉的机率就越大

pid_collection=$(ls /proc/ | grep -E “^[0-9]+$”)

printf “score\tpid\tname\tcmd\r\n” for pid in $pid_collection do score=$(cat /proc/$pid/oom_score 2>/dev/null) if [[ $score -gt 0 ]];then name=ps -ef | awk -vid=$pid '{if($2==id){print $1"\t"$8}}' printf “$score\t$pid\t$name\r\n” fi done ```

score pid name cmd 61 418440 root /usr/local/sbin/java 30 418510 root /usr/local/sbin/java 91 423091 root /usr/local/sbin/java 39 423178 root /usr/local/sbin/java 167 670299 artemis java 23 670741 artemis java 9 671741 postgres postgres: 9 671793 postgres postgres: 9 799 postgres postgres: 8 800 postgres postgres: 3 833 root /usr/local/aegis/aegis_client/aegis_11_25/AliYunDun

参考:
https://help.aliyun.com/document_detail/316785.html