OOM killer是linux内核在内存不足情况下的一种管理机制,当内核检测到系统物理内存不足时,就会通过OOM killer机制kill掉一些进程,kill进程的原则是通过使用一套启发式算法,它会计算所有进程的分数,然后选出那个分数最高的进程。进而kill掉,一般分数最高的进程占用的内存刚好是最大的。
那么为什么会突然出现内存不足的情况呢,这就要说说进程与内存的运行机制,默认情况下,Linux内核根据应用程序的要求分配内存,通常来说应用程序分配了内存但是并没有实际全部使用,为了提高性能,这部分没用的内存可以留作它用,这部分内存是属于每个进程的,内核直接回收利用的话比较麻烦,所以内核采用一种过度分配内存(over-commit memory)的办法来间接利用这部分 “空闲” 的内存,提高整体内存的使用效率。这个问题最类似的就是我们使用的宽带运营商了,比如现在电信宽带都承诺卖给用户的都是300Mb光纤带宽,而这实际上远远超出了他们的网络容量。他们赌的就是用户实际上并不会同时使用并用完分配给我们的带宽上限。
一般来说这样做没有问题,但当大多数应用程序都消耗完自己的内存的时候麻烦就来了,如果这些应用程序的内存需求加起来超出了物理内存(包括 swap)的容量,这就会导致系统可用内存迅速降低甚至不够用的情况,也就是没有内存页能够再分配给进程了。此时,内核必须kill掉一些进程才能腾出内存空间保障系统正常运行,于是,OOM killer机制被激活了,并通过启发式算法找出了要终结的进程。
理解了这个机制之后,我们就很容易理解了为啥java进程躺着也能中枪了,因为它在系统上一般占用内存最多,所以如果Out of Memeory (OOM) 的话总,是不幸第一个被kill掉的应用。
OOM killer机制也是可配置的,我们可以通过一些内核参数来调整OOM killer的行为,避免系统在那里不停的kill进程。kill进程是根据每个进程打分的高低来决定的,root 权限的进程通常被认为很重要,不应该被轻易杀掉,所以打分的时候可以得到 3% 的优惠,而我们可以在用户空间通过操作每个进程的oom_adj内核参数来降低哪些进程被OOM killer选中kill掉的几率。比如,如果不想java进程被轻易杀掉的话,可以找到java运行的进程号后,调整oom_score_adj的值即可,例如,查看系统pid为30522的进程oom_score的值,可以执行如下操作:
[root@hadoopgateway ~]# cat /proc/30522/oom_score22例如要调整此进程的adj值为-16的话,可以执行如下操作:
[root@hadoopgateway ~]#echo -16 > /proc/30522/oom_score_adj[root@hadoopgateway ~]# cat /proc/30522/oom_score6从这个操作过程中可以看到,在用户空间可以通过操作每个进程的oom_adj内核参数来调整进程的分数,而这个分数是通过oom_score这个内核参数看到的。
知道了oom_score_adj与oom_score的含义和设置后,可以写一个简单的脚本,查看下当前系统上oom_score分数最高(最容易被OOM Killer杀掉)的进程,脚本内容如下:
[root@hadoopgateway ~]# vi oom.sh#!/bin/bashfor proc in $(find /proc -maxdepth 1 -regex '/proc/[0-9] '); do printf "- ] %sn" "$(cat $proc/oom_score)" "$(basename $proc)" "$(cat $proc/cmdline | tr '0' ' ' | head -c 50)"done 2>/dev/null | sort -nr | head -n 10[root@hadoopgateway ~]# chmod 755 oom.sh[root@hadoopgateway ~]# ./oom.sh25 9571 /usr/java/default/bin/java -Xmx1000m -Djava.net.pr18 22193 /usr/java/default/bin/java -Xmx256m -Djava.net.pre16 21455 /usr/java/default/bin/java -Xmx256m -Djava.net.pre15 26121 /usr/java/default/bin/java -Xmx256m -Djava.net.pre13 17528 /usr/java/default/bin/java -Xmx256m -Djava.net.pre12 6071 /usr/java/default/bin/java -Xmx256m -Djava.net.pre12 18392 /usr/java/default/bin/java -Xmx256m -Djava.net.pre 1 933 /data/redis/bin/redis-server *:6379 1 9212 -bash 1 9210 sshd: root@pts/6这个输出中,pid为9571的进程,oom_score分数最高,最容易被OOM Killer杀掉。
当然,我们也可以完全关闭OOM killer,但不推荐用在生产环境下关闭,执行如下操作:
[root@hadoopgateway ~]#sysctl -w vm.overcommit_memory=2[root@hadoopgateway ~]# echo "vm.overcommit_memory=2" >> /etc/sysctl.confvm.overcommit_memory有0、1、2三个可选值,分别代表如下含义:
0:表示用户申请内存的时候,系统会判断剩余的内存多少,如果不够的话那么就会失败。1: 用户申请内存的时候,系统不进行任何内存是否够用的检查,直到使用内存超过可用内存。2: 用户一次申请的内存大小不允许超过可用内存的大小。
理解了OOM killer,就明白了为啥系统经常会出现内存溢出的现象了。好啦,今天就说到这里。