配置 vm.overcommit_memory 的效果

dun*_*nxd 41 linux web-server memory-usage linux-kernel

由于 oom-killer 启动,我的 VPS Web 服务器在 CentOS 5.4(Linux 内核 2.6.16.33-xenU)上不规则地运行(例如每月一次或需要几周)变得无响应。服务器的监控表明它没有通常用完内存,只是每隔一段时间。

我已经阅读了一些指向此页面的博客,其中讨论了使用以下 sysctl 设置来配置内核以更好地管理过度使用:

vm.overcommit_memory = 2
vm.overcommit_ratio = 80
Run Code Online (Sandbox Code Playgroud)

我对此的理解(这可能是错误的,但我找不到一个规范的定义来澄清)是,这可以防止内核过度分配超出交换区 + 80% 物理内存的内存。

但是,我还阅读了一些其他来源,表明这些设置不是一个好主意 - 尽管这种方法的批评者似乎在说“不要做破坏您的系统的事情,而不是尝试这种混乱”的假设因果关系总是已知的。

所以我的问题是,在托管大约 10 个低流量站点的 Apache2 Web 服务器的上下文中,这种方法的优缺点什么?在我的特定情况下,Web 服务器有 512Mb RAM,1024Mb 交换空间。大多数情况下,这似乎已经足够了。

phe*_*mer 35

设置overcommit_ratio为 80 可能不是正确的操作。将该值设置为小于 100 的任何值几乎总是不正确的。

这样做的原因是 linux 应用程序分配的比它们真正需要的多。假设他们分配了 8kb 来存储几个文本字符串。嗯,那里有几个 KB 未使用。应用程序经常这样做,这就是过度使用的目的。

因此,基本上在 100 时过量使用时,内核将不允许应用程序分配比您拥有的内存(交换 + ram)更多的内存。将其设置为小于 100 意味着您将永远不会使用所有内存。如果您要设置此设置,则应将其设置为高于 100,因为上述场景非常常见。
然而,虽然将其设置为大于 100 几乎总是正确的答案,但在某些用例中将其设置为小于 100 是正确的。如前所述,这样做您将无法使用所有内存。但是内核仍然可以。所以你可以有效地使用它来为内核保留一些内存(例如页面缓存)。

现在,至于您的 OOM 杀手触发问题,手动设置过量使用不太可能解决此问题。默认设置(启发式确定)相当智能。

如果您想看看这是否真的是问题的原因,请查看/proc/meminfoOOM 杀手何时运行。如果您看到Committed_AS接近CommitLimit,但free仍然显示可用内存,那么您可以为您的方案手动调整过量使用。将此值设置得太低将导致 OOM 杀手在您仍有大量可用内存时开始杀死应用程序。将它设置得太高会导致随机应用程序在尝试使用分配的内存时死亡,但实际上并不可用(当所有内存实际上都用完时)。

  • 您的 4 KB 示例是错误的。虚拟内存与页面一起使用,Linux 下页面的(最小)大小为 4 KB。这意味着存储几个字符需要 4 KB 映射到某处,而不管过度使用设置如何。内存过度承诺的一个适当示例是例如您分配 10 KB 并仅使用前 4100 个字节。这意味着需要两个 4 KB 页面来存储数据,而另外一个页面未使用。如果需求到达,非过度提交系统将始终准备好第三页来存储数据,过度提交系统不会强制执行。 (7认同)
  • @dunxd 您可以为此使用`/proc/<PID>/oom_score_adj`。例如,如果你为 sshd 设置 oom_score_adj 为 -1000,那么 oom 杀手在它想要杀死某些东西时永远不会以 sshd 为目标。完全停止oom杀手不是一个好主意,因为那样你的程序将无法分配内存,无论如何它们都会死。 (6认同)
  • @dunxd 它是继承的。让您的 init 脚本自行设置它,并且由 init 脚本启动的任何内容都会继承它。 (4认同)
  • 如果您运行的应用程序(例如 PostgreSQL)依赖于操作系统维护磁盘页面的 RAM 缓存,则将 overcommit_ratio 设置为 80 或 90 是有意义的。通常,您宁愿让操作系统开始交换而不是丢弃所有缓存。 (3认同)
  • /proc/self 指向当前进程,因此 /proc/self/oom_score_adj 可用于更改当前进程的 oom_score_adj。 (2认同)

Ale*_*eys 25

@dunxd 提到的文档中的第 9.6 节“过度使用和 OOM”特别生动地说明了允许过度使用的危险。然而,这80对我来说也很有趣,所以我进行了一些测试。

我发现这overcommit_ratio会影响所有进程可用的总 RAM。根进程似乎与普通用户进程没有区别对待。

将比率设置为100或更少应该提供经典语义,其中返回值malloc/sbrk是可靠的。将其比率设置为低于100可能是为非进程活动(如缓存等)保留更多 RAM 的一种方式。

因此,在我的计算机上,内存为 24 GiB,禁用交换,使用 9 GiB,top显示

Mem:  24683652k total,  9207532k used, 15476120k free,    19668k buffers
Swap:        0k total,        0k used,        0k free,   241804k cached
Run Code Online (Sandbox Code Playgroud)

以下是一些overcommit_ratio设置以及我的 ram-consumer 程序可以获取多少 RAM(触摸每个页面) - 在每种情况下,程序一旦malloc失败就会干净利落地退出。

 50    ~680 MiB
 60   ~2900 MiB
 70   ~5200 MiB
100  ~12000 MiB
Run Code Online (Sandbox Code Playgroud)

一次运行几个,即使有些人是 root 用户,也不会改变他们一起消耗的总量。有趣的是,它无法消耗最后 3+ GiB 左右;在free没降远低于此处显示的:

Mem:  24683652k total, 20968212k used,  3715440k free,    20828k buffers
Run Code Online (Sandbox Code Playgroud)

实验很混乱 - 在所有 RAM 都在使用的时候使用 malloc 的任何东西都倾向于崩溃,因为许多程序员对检查 C 中的 malloc 失败很糟糕,一些流行的集合库完全忽略它,而 C++ 和其他各种语言甚至更差。

大多数虚RAM我看到的早期实现的是处理一个非常特殊的情况下,在一个大的过程-比如可用内存的51%+ -需要fork()exec()一定的支持程序,通常是一个非常非常小的一个。具有写时复制语义的操作系统将允许fork(),但附带条件是,如果分叉进程实际上尝试修改太多内存页面(然后每个内存页面都必须实例化为独立于初始巨大进程的新页面)它最终会被杀死。父进程只有在分配更多内存时才会处于危险之中,并且可以处理耗尽,在某些情况下,只需等待一些其他进程死亡,然后继续。子进程通常只是通过一个(通常较小的)程序替换自己exec() 然后就没有附带条件了。

Linux 的过度使用概念是一种极端的方法,既允许fork()发生,也允许单个进程大规模过度分配。OOM杀手造成的死亡人数是异步的,甚至到节目处理内存分配负责任。我个人总体上讨厌系统范围的过度使用,尤其是 oom-killer - 它促进了一种恶魔般的内存管理方法,这种方法会感染库并通过它们感染使用它们的每个应用程序。

我建议将比率​​设置为 100,并且也有一个交换分区,这通常只会被大型进程使用 - 通常只使用自己被塞入交换的部分的一小部分,因此保护绝大多数进程免受 OOM 杀手错误功能的影响。这应该可以使您的网络服务器免受随机死亡的影响,并且如果它被编写为malloc负责任地处理,甚至可以避免自杀(但不要押注后者)。

这意味着我正在使用它 /etc/sysctl.d/10-no-overcommit.conf

vm.overcommit_memory = 2
vm.overcommit_ratio = 100
Run Code Online (Sandbox Code Playgroud)

  • 好消息 - 这确实是我正在使用的;我想我在答案中省略了它,因为它已经在问题中 (2认同)