Linux内存高,触发oom-killer问题解决

时间:2021-10-01 20:57:33

最近遇到两起Linux的内存问题,其一是触发了oom-killer导致系统挂

1. 首先确认该系统的版本是32位

#uname
-a
Linux
alarm 2.6.9-67.ELsmp #1 SMP Wed Nov 7 13:58:04 EST 2007 i686 i686 i386

2. 我们了解一下32位Linux的内存管理结构

#
DMA: 0x00000000 -  0x00999999 (0 - 16 MB)
#
LowMem: 0x01000000 - 0x037999999 (16 - 896 MB) - size: 880MB
#
HighMem: 0x038000000 - <硬件特定>

    内核使用low memory来跟踪所有的内存分配,这样的话一个16GB内存的系统比一个4GB内存的系统,需要消耗更多的low memory,当low memory耗尽,即便系统仍然有剩余内存,仍然会触发oom-killer。在2.6内核的表现是,杀掉占用内存最高的进程,所以会导致sshd等进程被杀掉,造成系统无法登录。

3. 如何查看lowMem

Linux内存高,触发oom-killer问题解决
-bash-3.00# free -lm
total used free shared buffers cached
Mem: 2026 1973 52 0 36 1303
Low: 874 823 51
High: 1151 1150 1
-/+ buffers/cache: 633 1392
Swap: 3067 142 2924
Linux内存高,触发oom-killer问题解决

4. 所以我们需要保护LowMem,在2.6内核引入了lower_zone_protection,这将让内核愿意保护low memory,从而在分配内存时优先考虑从high memory分配。

-bash-3.00#
cat /proc/sys/vm/lower_zone_protection
0
-bash-3.00#echo
400 > /proc/sys/vm/lower_zone_protection

 另一起问题是24G内存的系统,空闲内存已经不到50M

1. 确认该系统的版本是64位

#
uname -a
Linux
gxgd-nms-app 2.6.18-194.el5xen #1 SMP Tue Mar 16 22:01:26 EDT 2010 x86_64 x86_64 x86_64 GNU/Linux

2. 用ps查看各进程的内存,大约就占用了4G, 绝大部分内存都是被Page Cache所占用。Linux内核的策略是最大程度的利用内存cache 文件系统的数据,提高IO速度,虽然在机制上是有进程需要更大的内存时,会自动释放Page Cache,但不排除释放不及时或者释放的内存由于存在碎片不满足进程的内存需求。

   所以我们需要一个方法,能够限定PageCache的上限。

Linux 提供了这样一个参数min_free_kbytes,用来确定系统开始回收内存的阀值,控制系统的空闲内存。值越高,内核越早开始回收内存,空闲内存越高。

[root@zyite-app01
root]# cat /proc/sys/vm/min_free_kbytes
163840
echo
963840 > /proc/sys/vm/min_free_kbytes

其他可选的临时解决方法:

关闭oom-killer

cat /proc/sys/vm/oom-kill
echo "0" > /proc/sys/vm/oom-kill
vi /etc/sysctl.conf
  vm.oom-kill = 0

2. 清空cache (可选)
echo 1 > /proc/sys/vm/drop_caches




original link:http://www.cnblogs.com/itfriend/archive/2011/12/14/2287160.html



理解和配置 Linux 下的 OOM Killer


这几天用C++11写了个游戏服务器,跑在Windows/FreeBSD/Linux上都没问题,结果在Linux做压力测试的时候经常会挂掉,运行30分钟后系统提示Killed。

这通常是因为某时刻应用程序大量请求内存导致系统内存不足造成的,这通常会触发 Linux 内核里的 Out of Memory (OOM) killer,OOM killer 会杀掉某个进程以腾出内存留给系统用,不致于让系统挂掉。

如果检查相关的日志文件(/var/log/messages)就会看到下面类似的 Out of memory: Kill process 信息:

...
Apr 16 09:45:53 debian kernel: [76149.969715] Out of memory: Kill process 8487 (TCPServer) score 28 or sacrifice child
Apr 16 09:45:53 debian kernel: [76149.970289] Killed process 8487 (TCPServer) total-vm:162584kB, anon-rss:28912kB, file-rss:0kB
...

Linux 内核根据应用程序的要求分配内存,通常来说应用程序分配了内存但是并没有实际全部使用,为了提高性能,这部分没用的内存可以留作它用,这部分内存是属于每 个进程的,内核直接回收利用的话比较麻烦,所以内核采用一种过度分配内存(over-commit memory)的办法来间接利用这部分 “空闲” 的内存,提高整体内存的使用效率。一般来说这样做没有问题,但当大多数应用程序都消耗完自己的内存的时候麻烦就来了,因为这些应用程序的内存需求加起来超 出了物理内存(包括 swap)的容量,内核(OOM killer)必须杀掉一些进程才能腾出空间保障系统正常运行。用银行的例子来讲可能更容易懂一些,部分人取钱的时候银行不怕,银行有足够的存款应付,当 全国人民(或者绝大多数)都取钱而且每个人都想把自己钱取完的时候银行的麻烦就来了,银行实际上是没有这么多钱给大家取的。
内核检测到系统内存不足、挑选并杀掉某个进程的过程可以参考内核源代码  linux/mm/oom_kill.c , 当系统内存不足的时候,out_of_memory() 被触发,然后调用 select_bad_process() 选择一个 “bad” 进程杀掉,如何判断和选择一个 “bad” 进程呢,总不能随机选吧?挑选的过程由 oom_badness() 决定,挑选的算法和想法都很简单很朴实:最 bad 的那个进程就是那个最占用内存的进程。
/*** oom_badness - heuristic function to determine which candidate task to kill
* @p: task struct of which task we should calculate
* @totalpages: total present RAM allowed for page allocation
*
* The heuristic for determining which task to kill is made to be as simple and
* predictable as possible.  The goal is to return the highest value for the
* task consuming the most memory to avoid subsequent oom failures.
*/

unsigned long oom_badness(struct task_struct *p, struct mem_cgroup *memcg,
                         
const nodemask_t *nodemask, unsigned long totalpages)
{
       
long points;
       
long adj;

       
if (oom_unkillable_task(p, memcg, nodemask))
               
return 0;

        p
= find_lock_task_mm(p);
       
if (!p)
               
return 0;

        adj
= (long)p->signal->oom_score_adj;
       
if (adj == OOM_SCORE_ADJ_MIN) {
                task_unlock
(p);
               
return 0;
       
}

       
/*
         * The baseline for the badness score is the proportion of RAM that each
         * task's rss, pagetable and swap space use.
         */

        points
= get_mm_rss(p->mm) + p->mm->nr_ptes +
                 get_mm_counter
(p->mm, MM_SWAPENTS);
        task_unlock
(p);

       
/*
         * Root processes get 3% bonus, just like the __vm_enough_memory()
         * implementation used by LSMs.
         */

       
if (has_capability_noaudit(p, CAP_SYS_ADMIN))
                adj
-= 30;

       
/* Normalize to oom_score_adj units */
        adj
*= totalpages / 1000;
        points
+= adj;

       
/*
         * Never return 0 for an eligible task regardless of the root bonus and
         * oom_score_adj (oom_score_adj can't be OOM_SCORE_ADJ_MIN here).
         */

       
return points > 0 ? points : 1;
}

上面代码里的注释写的很明白,理解了这个算法我们就理解了为啥 MySQL 躺着也能中枪了,因为它的体积总是最大(一般来说它在系统上占用内存最多),所以如果 Out of Memeory (OOM) 的话总是不幸第一个被 kill 掉。解决这个问题最简单的办法就是增加内存,或者想办法优化 MySQL 使其占用更少的内存,除了优化 MySQL 外还可以优化系统,让系统尽可能使用少的内存以便应用程序(如 MySQL) 能使用更多的内存,还有一个临时的办法就是调整内核参数,让 MySQL 进程不容易被 OOM killer 发现。
配置 OOM killer

我们可以通过一些内核参数来调整 OOM killer 的行为,避免系统在那里不停的杀进程。比如我们可以在触发 OOM 后立刻触发 kernel panic,kernel panic 10秒后自动重启系统。
# sysctl -w vm.panic_on_oom=1vm.panic_on_oom = 1

# sysctl -w kernel.panic=10
kernel
.panic = 10

# echo "vm.panic_on_oom=1" >> /etc/sysctl.conf
# echo "kernel.panic=10" >> /etc/sysctl.conf

从上面的 oom_kill.c 代码里可以看到 oom_badness() 给每个进程打分,根据 points 的高低来决定杀哪个进程,这个 points 可以根据 adj 调节,root 权限的进程通常被认为很重要,不应该被轻易杀掉,所以打分的时候可以得到 3% 的优惠(adj -= 30; 分数越低越不容易被杀掉)。我们可以在用户空间通过操作每个进程的 oom_adj 内核参数来决定哪些进程不这么容易被 OOM killer 选中杀掉。比如,如果不想 MySQL 进程被轻易杀掉的话可以找到 MySQL 运行的进程号后,调整 oom_score_adj 为 -15(注意 points 越小越不容易被杀):
# ps aux | grep mysqldmysql   2196  1.6  2.1 623800 44876 ?           Ssl  09:42   0:00 /usr/sbin/mysqld

# cat /proc/2196/oom_score_adj
0
# echo -15 > /proc/2196/oom_score_adj

当然,如果需要的话可以完全关闭 OOM killer(不推荐用在生产环境):
# sysctl -w vm.overcommit_memory=2
# echo "vm.overcommit_memory=2" >> /etc/sysctl.conf

找出最有可能被 OOM Killer 杀掉的进程

我们知道了在用户空间可以通过操作每个进程的 oom_adj 内核参数来调整进程的分数,这个分数也可以通过 oom_score 这个内核参数看到,比如查看进程号为8487的 omm_score,这个分数被上面提到的 omm_score_adj 参数调整后(-15),就变成了3:
# cat /proc/8487/oom_score18

# echo -15 > /proc/8487/oom_score_adj
# cat /proc/8487/oom_score
3

下面这个 bash 脚本可用来打印当前系统上 oom_score 分数最高(最容易被 OOM Killer 杀掉)的进程:
# vi oomscore.sh#!/bin/bash
for proc in $(find /proc -maxdepth 1 -regex '/proc/[0-9]+'); do
        printf
"%2d %5d %s\n" \
               
"$(cat $proc/oom_score)" \
               
"$(basename $proc)" \
               
"$(cat $proc/cmdline | tr '\0' ' ' | head -c 50)"
done 2>/dev/null | sort -nr | head -n 10

# chmod +x oomscore.sh
# ./oomscore.sh
18   981 /usr/sbin/mysqld
4 31359 -bash
4 31056 -bash
1 31358 sshd: root@pts/6
1 31244 sshd: vpsee [priv]
1 31159 -bash
1 31158 sudo -i
1 31055 sshd: root@pts/3
1 30912 sshd: vpsee [priv]
1 29547 /usr/sbin/sshd -D

above link:  http://community.itbbs.cn/thread/758114/