
# 如何处理Linux实例中的OOM问题？
#### 背景信息
OOM（Out of Memory）指系统内存已用完，在Linux系统中，如果内存用完会导致系统无法正常工作，触发系统panic或者OOM Killer。OOM Killer是Linux内核的一个机制，该机制会监控那些占用内存过大的进程，尤其是短时间内消耗大量内存的进程，在系统的内存即将不够用时结束这些进程从而保障系统的整体可用性。
#### 问题现象
ECS实例在运行过程中日志中有类似于如下所示信息：
```
[  327.199955] 5202 total pagecache pages
[  327.200965] 0 pages in swap cache
[  327.201950] Swap cache stats: add 0, delete 0, find 0/0
[  327.203656] Free swap  = 0kB
[  327.204580] Total swap = 0kB
[  327.205491] 2096926 pages RAM
[  327.206375] 0 pages HighMem/MovableOnly
[  327.207509] 94837 pages reserved
[  327.208746] [ pid ]   uid  tgid total_vm      rss nr_ptes swapents oom_score_adj name
[  327.210727] [  352]     0   352     9764      616      22        0             0 systemd-journal
[  327.212897] [  372]     0   372    11370      150      23        0         -1000 systemd-udevd
[  327.214662] [  410]     0   410    13883      102      27        0         -1000 auditd
[  327.216276] [  498]    81   498    14558      164      33        0          -900 dbus-daemon
[  327.217880] [  499]     0   499     5385       62      16        0             0 irqbalance
[  327.219369] [  500]   999   500   153060     1350      63        0             0 polkitd
[  327.221158] [  502]     0   502   243884     3537      36        0             0 uniagent
[  327.222916] [  503]     0   503   137033     1033      84        0             0 NetworkManager
[  327.225002] [  505]     0   505     6596       77      19        0             0 systemd-logind
[  327.226841] [  508]   998   508    29452      105      30        0             0 chronyd
[  327.228427] [  529]     0   529    26123      488      21        0             0 uniagentd
[  327.230120] [  532]     0   532   143572     2883      96        0             0 tuned
[  327.231779] [  563]     0   563    25753      514      51        0             0 dhclient
[  327.233396] [  608]     0   608    54632      688      42        0             0 rsyslogd
[  327.235004] [  839]     0   839    39781      400      42        0             0 hostwatch
[  327.236505] [  904]     0   904   335577     1654     112        0             0 [kworker/0:0H]
[  327.237967] [ 1059]     0  1059    22452      253      45        0             0 master
[  327.239371] [ 1074]    89  1074    22478      250      44        0             0 pickup
[  327.240758] [ 1075]    89  1075    22495      252      45        0             0 qmgr
[  327.242087] [ 1416]     0  1416    28251      259      57        0         -1000 sshd
[  327.243474] [ 1418]     0  1418     6477       51      18        0             0 atd
[  327.244773] [ 1420]     0  1420    31597      152      18        0             0 crond
[  327.246133] [ 1422]     0  1422    24842      166      51        0             0 login
[  327.247893] [ 1423]     0  1423    27552       32      10        0             0 agetty
[  327.249319] [ 2359]     0  2359   197550     4903      48        0             0 containerserver
[  327.250828] [ 7726]     0  7726    63736      535      22        0             0 uniagentd
[  327.252278] [ 7727]     0  7727    98075      305      26        0             0 uniagentd
[  327.253688] [ 7773]     0  7773    28886      101      13        0             0 bash
[  327.254906] [ 7840]     0  7840   225333     3183      34        0             0 telescope
[  327.256240] [ 7922]     0  7922    39390      374      83        0             0 sshd
[  327.257496] [ 7924]     0  7924    28887      102      13        0             0 bash
[  327.258769] [ 7943]     0  7943    18088      190      39        0             0 sftp-server
[  327.260041] [ 8076]     0  8076  1958387  1904796    3739        0             0 python3
[  327.261301] Out of memory: Kill process 8076 (python3) score 924 or sacrifice child
[  327.262504] Killed process 8076 (python3), UID 0, total-vm:7833548kB, anon-rss:7619112kB, file-rss:72kB, shmem-rss:0kB
```
#### 可能原因
如果您在Linux实例上运行程序时，频繁发生OOM，可能是以下原因。
- Linux实例的配置过低，无法满足程序运行所需的内存资源，引发OOM。
- 应用程序代码对内存的使用不当，引发OOM。
 
#### 处理方法
关于OOM的问题，请参考以下步骤进行处理：
1. 执行以下命令，查看OOM相关日志。
   ```
   sudo less /var/log/messages    # CentOS 
   sudo less /var/log/syslog      # Ubuntu
   ```
   在日志中通过oom或者kill作为关键字进行搜索，以查看OOM问题的相关日志信息。
   日志中会存在OOM Killer评估进程时的标准输出标题行如下图，其中可通过对rss列（单位4KB）进行排序，查看内存消耗最大的进程。
   ![](https://support.huaweicloud.com/trouble-ecs/zh-cn_image_0000002442240100.png "点击放大")
   如果是业务进程占用的内存高导致的内存OOM，建议您可以进一步通过业务应用日志定位内存占用高的原因。
   
2. 根据您排查的结果，可以选择对应的处理方案。
   
   | **问题原因**                      | **处理建议**                                                                                                                                                                                                              |
   |:---|:---|
   | Linux实例的配置过低，无法满足程序运行所需的内存资源。 | 升级实例内存配置：[变更ECS规格](https://support.huaweicloud.com/usermanual-ecs/ecs_03_9005.html)。 注意： 更改实例规格的过程中，您需要停止并重新启动ECS实例，建议您在非业务高峰期时执行该操作，减少对您业务造成的影响。 |
   | 应用程序代码对内存的使用不当。               | 优化应用程序代码，如对批量查询请求进行分页等。                                                                                                                                                                                               |
      
   
 
#### 参考信息
1. OOM Killer评估进程时的标准输出标题行，各列具体详解如[表1 OOM Killer评估进程时的标准输出标题行]所示。
    表1OOM Killer评估进程时的标准输出标题行 
   | 列名            | 全称                           | 含义解释                                                                                                                                                                                                                                                                            |
   |:---|:---|:---|
   | pid           | Process ID                   | 进程的操作系统内部ID。                                                                                                                                                                                                                                                                    |
   | uid           | User ID                      | 启动该进程的用户ID。0代表root用户。                                                                                                                                                                                                                                                           |
   | tgid          | Thread Group ID              | 线程组ID。 - 对于单线程进程，这与pid相同。  - 对于多线程程序，所有线程共享同一个tgid。   |
   | total_vm      | Total Virtual Memory         | 进程占用的总虚拟内存大小（以4KB的页为单位）。这个值很大，因为它包括共享库、映射的文件等，不是衡量内存压力的最佳指标。                                                                                                                                                                                                                    |
   | rss           | Resident Set Size            | 驻留集大小（以4KB的页为单位）。这是进程当前在物理内存中占用的、非交换的内存总量。这是 OOM Killer 最重要的考量因素之一，它直接反映了进程对物理内存的消耗。                                                                                                                                                                                            |
   | nr_ptes       | Number of Page Table Entries | 页表项数目。进程使用的虚拟内存越多，需要的页表项就越多。这项本身也消耗内存，所以也是 OOM 评分的一个因素。                                                                                                                                                                                                                         |
   | swapents      | Swap Entries                 | 被换出到交换分区（Swap）的内存大小（以4KB的页为单位）。值越高，说明该进程的一些内存当前不在物理RAM中。                                                                                                                                                                                                                        |
   | oom_score_adj | OOM Score Adjust             | OOM调整值（范围从-1000到1000）。这是用户态可以设置的最重要的参数，用于主动影响 OOM Killer的决策。                                                                                                                                                                                                                    |
   | name          | Process Name                 | 进程的可执行文件名称。                                                                                                                                                                                                                                                                     |
      
   
2. OOM相关参数如[表2 OOM相关参数表]所示。
    表2OOM相关参数表 
   | 参数名称                     | 参数说明                                                                                                                                                                                                                                                                                                                                                     | 取值                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | 修改方式                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
   |:---|:---|:---|:---|
   | panic_on_oom             | panic_on_oom参数是控制系统遇到OOM时如何反应的。当系统遇到OOM的时候，通常会有两种选择： - 触发系统panic，可能会出现频繁宕机的情况。  - 选择一个或者几个进程，触发OOM Killer，结束选中的进程，释放内存，让系统保持整体可用。   | 可以通过以下命令查看参数取值： **cat /proc/sys/vm/panic_on_oom**或者 **sysctl -a \| grep panic_on_oom** - 值为0：内存不足时，触发OOM Killer。  - 值为1：内存不足时，根据具体情况可能发生kernel panic，也可能触发OOM Killer。  - 值为2：内存不足时，强制触发系统panic，导致系统重启。                                                                                                                                                                       | 例如：将参数设置为0，可用以下两种方式： - 临时配置，立即生效，但重启后恢复成默认值。 **sysctl -w vm.panic_on_oom=0**     - 持久化配置，系统重启仍生效。 执行命令**vim /etc/sysctl.conf** ，在该文件中添加一行**vm.panic_on_oom =0** ，再执行命令**sysctl --p**或重启系统后生效。                     |
   | oom_kill_allocating_task | 当系统选择触发OOM Killer，试图结束某些进程时，oom_kill_allocating_task参数会控制选择哪些进程，有以下两种选择： - 触发OOM的进程。  - oom_score得分最高的进程。                              | 可以通过以下命令查看参数取值： **cat /proc/sys/vm/oom_kill_allocating_task** 或者**sysctl -a \| grep oom_kill_allocating_task** - 值为0：选择oom_score得分最高的进程。  - 值为非0：选择触发OOM的进程。                                                                                                                                                                                                                                                                                                                        | 例如：将该参数设置成1，可用以下两种方式： - 临时配置，立即生效，但重启后恢复成默认值。 **sysctl -w vm.oom_kill_allocating_task=1**     - 持久化配置，系统重启仍生效。 执行命令**vim /etc/sysctl.conf** ，在该文件中添加一行**vm.oom_kill_allocating_task=1** ，再执行命令**sysctl --p**或重启系统后生效。    |
   | oom_score                | 指进程的得分，主要由两部分组成： - 系统打分，主要是根据该进程的内存使用情况由系统自动计算。  - 用户打分，也就是oom_score_adj，可以自定义。                                                            | 可以通过调整oom_score_adj的值进而调整一个进程最终的得分。通过以下命令查看参数取值： **cat /proc/*进程id*/oom_score_adj** - 值为0：不调整oom_score。  - 值为负值：在实际打分值上减去一个折扣。  - 值为正值：增加该进程的oom_score。                                                                                                                                                                                                                                                 | 例如：将进程id为2939的进程oom_score_adj参数值设置为1000，可用以下命令： **echo 1000 \> /proc/2939/oom_score_adj**                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
   | oom_dump_tasks           | oom_dump_tasks参数控制OOM发生时是否记录系统的进程信息和OOM Killer信息。 例如dump系统中所有的用户空间进程关于内存方面的一些信息，包括：进程标识信息、该进程使用的内存信息、该进程的页表信息等，这些信息有助于了解出现OOM的原因。                                                                                                                                                                                       | 可以通过以下命令查看参数取值： **cat /proc/sys/vm/oom_dump_tasks** 或者**sysctl -a \| grep oom_dump_tasks** - 值为0：OOM发生时不会打印相关信息。  - 值为非0：以下三种情况会调用dump_tasks打印系统中所有task的内存状况。 - 由于OOM导致kernel panic。  - 没有找到需要结束的进程。  - 找到进程并将其结束的时候。     | 例如：将该参数设置成0，可用以下两种方式： - 临时配置，立即生效，但重启后恢复成默认值。 **sysctl --w vm.oom_dump_tasks=0**   - 持久化配置，系统重启仍生效。 执行命令**vim /etc/sysctl.conf** ，在该文件中添加一行**vm.oom_dump_tasks=0** ，再执行命令**sysctl --p**或重启系统后生效。                                                              |
      
   
 
