如何优雅地使用Linux系统OOM ( Out Of Memory Killer)

如何优雅地使用Linux系统OOM ( Out Of Memory Killer),第1张

Linux内核根据服务器上当前运行应用的需要来分配内存。因为这通常是预先发生的,所以应用并不会使用所有分配的内存。这将会导致资源浪费,Linux内核允许超分内存以提高内存使用效率。Linux内核允许超分内存,比如总共8G内存,可以分给10个进程各1G,这通常没问题。但问题发生在太多应用一起占用内存,有8个进程各占了1G,剩下两个进程要喝西北风了。

由于内存不足,服务器有崩溃的风险。The  server runs the risk of crashing because it  ran out of memory。为了防止服务器到达这个临近状态,内核中有一个OOM Killer杀手进程。To prevent the server from reaching  that critical state, the kernel also contains a process known as the OOM Killer。内核利用这个杀手进程开始屠杀那些非必要进程,以便服务器正常运行。The kernel uses this  process to start killing non-essential  processes so the server can remain  operational.

当你认为这一切都不是问题时,因为OOM Killer只杀掉那些非必要的,不是用户需要的进程。举例,两个应用(Apache和MySQL)通常先被杀掉,因为占用大量的内存。但这将导致一个web网站立马瘫痪了。

当尝试找到为什么一个应用程序或进程被OOM killer杀掉时,有很多地方可以找到一个进程如何被杀掉以及被杀掉的原因。

$ grep -i kill /var/log/messages*

host kernel: Out of Memory: Killed process 5123 (exampleprocess)

The capital K in Killed tells you that the process was killed with a -9 signal, and this typically is a good indicator that the OOM Killer is to blame.

$ free -lh

The -l switch shows high and low memory  statistics, and the -h switch puts the output  into gigabytes for easier human readability. You can change this to the -m switch if you  prefer the output in megabytes.

同时该命令会给出Swap内存使用信息。注意:free命令给出某个时刻得数据,需要多执行几次才能知道内存动态的占用情况。

$ vmstat -SM 10 20

20次,每次间隔10秒给出内存使用情况。

top 默认输出CPU的使用情况,不过你可以在top后再按下shift + M,你将得到内存的使用情况。

配置文件/etc/sysctl.conf:

sysctl vm.panic_on_oom=1

sysctl kernel.panic=X

echo “vm.panic_on_oom=1” >>/etc/sysctl.conf

echo “kernel.panic=X” >>/etc/sysctl.conf

大多数情况下,内存不足时每次都重启是不合适的。

既可以保护一些重要进程不被OMM killer杀掉,又可以让不重要的进程更容易杀掉:

echo -15 >/proc/(PID)/oom_adj (不被杀)

echo 10 >/proc/(PID)/oom_adj (更易杀)

pstree -p | grep "process" | head -1

在某些情况下,豁免进程可能导致意外的行为变化,取决于系统和资源配置。假如内核无法杀死一个占用大量内存的进程,将杀死其他进程,包括那些重要的 *** 作系统进程。

由于OOM killer可调节的有效范围在-16到+15之间,设置为-17将豁免一个进程,因为在OOM killer调节范围之外。通常的规则是这个参数越大越容易被杀死豁免一个进程的命令是

echo -17 >/proc/(PID)/oom_adj

警告:不建议用于生产环境。

假如重启,修改进程优先级,豁免一个进程不足够好,有个风险的选项:将oom killer 功能关闭。

这一选项参数将有如下影响:

4.1) 严重的内核恐慌kernel panic

4.2) 系统挂住system hang-up

4.3) 一个完整的系统崩溃system crash

为什么关闭有风险呢呢?该功能避免自己因资源而跑飞了。如果你关闭此功能,将不能避免内存耗尽。考虑此项时请极度慎重。

sysctl vm.overcommit_memory=2

echo “vm.overcommit_memory=2” >>/etc/sysctl.conf

这个问题我想应该是仁者见仁智者见智的,所以没有必要diss别人的回答

大部分人都可以在自己的经验范围内给出一个合理的参考做法,这是没有问题的,而且应该没有人会说自己是标准答案,因为这本身就没有标准答案。所以我下面不会说“应该设置为XXX”这种话,如果你单纯对此有所期待,那么可以不用费时间向下阅读。

既然这个问题推到我这了,我也凑个热闹稍微说一个个人看法。首先我不是Windows用户,但是虚拟内存不是只有Windows用户才会面临的概念。因为虚拟内存本身是一个 *** 作系统层面的通用概念。在说如何设置虚拟内存之前,我先简单说一下什么是虚拟内存。

先来看一个例子,有这样一个程序:

我们用这个程序来申请内存分配,先来看一下我当前机器的内存情况:

可以看到物理内存有15G左右,当前有12G available,交换空间或者说这个题目所指的“虚拟内存”有7.6G。那么大家觉得我申请分配多少,或导致no memory的错误呢?

首先一次性申请不超过物理内存应该是没有问题的,那一次性申请超过物理内存,但是小于物理内存加“虚拟内存”的总大小呢?

没有问题,返回成功。那我们一次性申请超过物理内存加“虚拟内存”的总和呢?

这就不行了,系统直接返回错误,告诉你不能分配那么多内存。那我们每次申请5GiB,连续申请十次呢?

都没有问题,全部成功。为什么一次性申请50G不行,分批就可以呢?因为一次性申请50G,系统直接就能判定没有这么多。但是如果你分开使用,虽然我们每次都申请5G,但系统并没有真的给我们5G,只有在真的需要写入的时候,才会真的让这5G对应物理地址。我们现在可以看到,即使我们做了5G * 10的malloc *** 作,内存的状态仍然是没有变化:

说明这5G * 10的内存根本没有实际分配。为了让内存得到实际分配,我们对malloc出来的内存进行写入 *** 作,将程序改为:

这回我们先试试分配并写5GB:

可以看到物理内存的used从2.2变成7.2G。如果我们要分配并写超过物理内存大小,但是小于物理内存加上“虚拟内存”大小呢?

我们可以看到这么做也是可以的,而且物理内存已经基本全部占用,“虚拟内存”也占用了很大一部分。这时候我写这个回答也变得有一点点卡顿了,因为我的内存已经所剩无多了。但还可以接受,而且渐渐缓解,因为我当前写回答的进程是主要活跃进程,mytest那个测试进程占用的内存应该被部分换出到硬盘上的虚拟内存去了,给我当前的浏览器进程预留了足够的物理内存。

在虚拟内存已经所剩无多的状态下,我们尝试超额申请内存试试,我们一共加起来也就剩3G左右了,我们申请并写入5G试试。注意此时是有风险的,不同的系统可能会有不同的应对方式。在实际这么做之前,我预计我的系统(kernel-5.3.11-100.fc29.x86_64)应该会触发OOM killer,杀死系统认为大量浪费占用内存的进程,以维持系统正常运行(当然不排除误杀我当前的浏览器进程……但我认为概率不大,毕竟上面有一个占了17G的无用进程顶在我前面呢:)。让我们试试看(我先保存个草稿):$ ./mytest 5

在经过了一小段时间的卡顿后,这个程序执行成功了,我的浏览器没有被杀死,系统也没有崩溃。而且在预料之内的,那个占用17G内存的程序被内核主动杀死:

通过上述例子,我们可以对物理内存和“虚拟内存”有一个大概的印象。下面我们就来简单说一下什么是虚拟内存。

首先对于计算机技术来说,“虚拟内存”这个概念首先是指一种内存管理的技术方式,而不是指某一个“东西”。虚拟内存顾名思义,就是“虚构的”内存地址空间。这种虚构基于一种映射,进程在运行时面向的地址空间实际上是一层逻辑抽象的连续地址,每一个地址下面可能对应散落的物理内存,可能对应硬盘空间等,当然也可能什么都不对应,下面暂时是空的。在实际进行分配读写时,给需要实际物理内存的虚拟地址分配实际物理内存,暂时不需要实际物理内存的可以暂不分配,物理内存不够时也可以将已经分配的但是暂时不用的内存页写回硬盘,释放其所占用的实际物理内存。这样就保证程序在使用“虚拟”内存地址时不必过于担心实际的物理内存不够的情况。举个不是特别对等的例子,就好像是一个人用三块砖头垫在脚底下走过一片泥泞的道路一样,对于这个人来说他面向的就是一整片连续的道路(虚拟内存),虽然实际砖块(物理内存)很少,但是他可以通过不断把走过的地方的砖往前移动到没走过的地方,来达到一种近似“脚下总有路可以走”的效果。这实际上是虚拟内存的主要目的之一,让进程总觉得自己“有路可走”。甭管里子多忙多疲于奔命,面子上总要先过得去, 这就是好面子的虚拟内存 :)

然后我们在说一下这个问题中,以及日常经常被人提到的“虚拟内存”。上面我们说了,在虚拟内存技术中,硬盘等外部存储介质可以充当虚拟内存地址的临时媒介。所以开始有人将这部分外部存储空间称为虚构的内存,也简称为虚拟内存。所以这里的虚拟内存不是指一种技术,而是指一个东西。当然也有的人将整个内存地址空间都叫“虚拟内存”,因为他们认为使用的本来就是虚拟内存地址,不是实际物理地址。所以“虚拟内存”这个词现在变得很难单独提出来说是指什么,必须在较明确的上下文语境中才能“猜测”其具体代指什么。对于这个问题的语境,它应该是指除物理内存以外,用于交换物理内存页的,位于外存的交换空间,常见的称呼可能包括“虚拟内存”,“交换空间”、“虚存文件”、“页面文件”等等等等。

在早期,程序员处理内存不足的方式是使用一种近乎手动的覆盖技术,比如在几百K的内存空间上需要运行几M大小的程序,程序员需要将这个几M的程序在程序设计和编写时就手动分割,将它们分割为可以独立运行很多部分,然后让它们分时运行,每部分运行时都不超过内存大小。这种技术在现在实际上也没有完全意义上的消亡,这仍然是很多大型程序设计时应该考虑的。

不过这个和我们要说的“虚拟内存”这个东西没太多关系,我们要说的是虚拟内存技术的第二种,自动交换技术。交换技术的目的就是为了让正在或需要马上运行的程序能获得足够的物理内存资源,让不需要运行,或即将退出运行周期的进程让出占用的物理内存资源,将它们暂时保存到外存中去。这个技术就是由内存管理提供的功能,而不需要程序员手动干预。这就有点像你和另一个人分摊房费,分时租住同一个房间似的。

随着技术的发展,我们现在一般不需要以整个进程为粒度进行整体的换入换出,程序的运行也不需要一次性全加载进内存,运行的过程也不需要全部程序都在物理内存上。但是频繁换入换出仍然是性能的瓶颈。为了减缓换入换出带来的影响,增加可用物理内存和增加外存的访问速度是两种可选方案,而且这两种方案不冲突,有条件的完全可以同时选择。说简单了就是买更多的内存,和买更快的SSD等设备。

说了这么多,那么我们回过头说虚拟内存设置,也就是说我们设置多少的用于交换的外存空间更合适呢?可能有1.5~3倍左右物理内存这种说法。但是这个数字不是绝对的,也没有特别大的参考性。一般来说使用默认值就行,个人觉得0.5~2倍都在理论可接受范围内,你也要根据自己的实际内外存储情况进行评估,不能简单的说多少就合适。还是那句话,不懂就默认,除非默认的出现很大问题。如果你发现默认设置下系统卡顿严重,那么你可以尝试通过调整虚拟内存大小来尝试找到一个合适你使用习惯的更好的“值”。这将是综合专业分析的过程,或者是一个“瞎猫碰死耗子”的过程。有时通过调整,确实能达到一定的改善作用,但不绝对,也行你现在默认的就已经是很好的状态了。这时候说明你需要的就是增加物理内存大小,以及换更快的外存储器。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/tougao/11334597.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-15
下一篇 2023-05-15

发表评论

登录后才能评论

评论列表(0条)

保存