OOM排查与优化

OOM机制

        OOM指当程序占用内存超过系统内存或系统可调用的内存时,触发LINUX 内核机制(OOM killer),导致某些进程被kill掉;举个栗子,公司里同时有小明与小斌等若干员工,其中公司内部有一个打分机制,就是员工的干饭量(内存),本身公司是能负担所有员工,但这时小明与小斌尤为突出,突然之间干饭量孟增,为了保证公司不倒闭以及维持其他人能继续干饭,公司就通过这个干饭量进行打分,谁的分高就开除谁,然后发现,小明最能干饭,那么公司就把小明开了

OOM打分

       

         Linux内核有个机制叫OOM killer(Out-Of-Memory 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: taskstruct of which task we should calculate

 * @totalpages:total present RAM allowed for page allocation

 *

 * The heuristicfor determining which task to kill is made to be as simple and

 * predictableas possible.  The goal is to return thehighest value for the

 * task consumingthe most memory to avoid subsequent oom failures.

 */

unsigned long oom_badness(struct task_struct *p,struct mem_cgroup *memcg,

                                  const nodemask_t *nodemask, unsigned longtotalpages)

{

           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 thebadness score is the proportion of RAM that each

            * task's rss, pagetable and swap space use.

            */

           points = get_mm_rss(p->mm) + atomic_long_read(&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 -= (points * 3) / 100;

           /*Normalize to oom_score_adj units */

           adj *= totalpages / 1000;

           points += adj;

           /*

            * Never return 0 for an eligible taskregardless of the root bonus and

            * oom_score_adj (oom_score_adj can't beOOM_SCORE_ADJ_MIN here).

            */

           returnpoints > 0 ? points : 1;

}

        从上面的 oom_kill.c 代码里可以看到 oom_badness() 给每个进程打分,根据 points 的高低来决定杀哪个进程,这个 points 可以根据 adj 调节,root 权限的进程通常被认为很重要,不应该被轻易杀掉,所以打分的时候可以得到 3% 的优惠(分数越低越不容易被杀掉)。我们可以在用户空间通过操作每个进程的 oom_adj 内核参数来决定哪些进程不这么容易被 OOM killer 选中杀掉。

OOM相关参数

        

1、/proc/sys/vm/panic_on_oom(默认值为0)

 # sysctl -w vm.panic_on_oom=1
     vm.panic_on_oom = 1 //1表示关闭,默认为0表示开启OOM killer

   # sysctl –p

2、/proc/sys/vm/oom_kill_allocating_task(默认值为0)

        

    值为0:会根据OOM的打分机制来kill最占内存的进程

 值为非0:会直接kill掉由于申请内存而触发OOM的进程,不通过打分机制

3、oom_adj、oom_score_adj 和 oom_score

三个参数位置默认都是在 /proc/进程PID/ 目录下

OOM会根据进程里面的这三个参数进行打分,综合所有进程得到最高分的进程将会触发OOM 被kill掉,
进程的打分机制主要包括两部分: 系统打分和用户打分
系统打分就是根据进程的物理内存消耗量,然后内核会给出分数,放在oom_score里面(根据进程的内存使用情况值会跟着变)

用户打分就是修改oom_score_adj或者oom_adj的值,然后会跟oom__score进行加法运算得出最后的分数,
(oom_score_adj可选值范围为 -1000 - 1000,)(oom_adj可选值范围为 -17 - 15,)
oom_adj是旧版本供给用户修改的值,为了兼容,目前仍然保留这个参数,修改这个值,实际上是会换算成oom_score_adj的值。
最后OOM会对比所有进程的oom_score参数来决定该kill哪个进程。

4、/proc/sys/vm/oom_dump_tasks(默认值为1)

 如果将参数置为0,不会打印系统内进程的信息在/var/log/messages里面。
 值为1时:当有进程被OOM的时候,会输出进程的一些信息在日志里面(/var/log/messages),这些信息可以帮助找出为什么OOM,找到导致OOM的进程,以及可以看到为什么进程会被选中,只要发生OOM,都会打印进程的信息

OOM排查

        一般来说,我们会直接查看messages日志,是否存在类似out of memory字样,

          #cat /var/log/messages | grep -i "out of memory"

        还包括其他方法

       通过dmsge查看

          # dmesg | grep oom

       通过journalctl查看,响应信息会标红且会包含cgroup字样 

          # sudo journalctl

OOM解决办法

        最好的办法就是扩充资源,其次就是程序使用root执行(不推荐)

        然后就是调整程序oom_adj参数为-17 (参见前面内容)

        如果需要的话可以完全不允许过度分配内存,此时也就不会出现OOM的问题(不过不推荐这样做):
              # sysctl -w vm.overcommit_memory=2

补充:

   1.子进程会继承父进程的oom_adj。


   2.OOM不适合于解决内存泄漏(Memory leak)的问题。

   3.有时free查看还有充足的内存,但还是会触发OOM,是因为该进程可能占用了特殊的内存地址空间。(例如/run 下存在大文件)


版权声明:本文为weixin_57400332原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。