Out of memory: Kill process 解决

环境:Centos7

现象:Linux测试服务器上部署了很多程序mysql、mongodb、java等等。程序操作mongodb经常进程被杀死的情况,导致业务中断,mongodb 的logs无提示信息。查看系统日志message后,发现对应时间点,系统自动kill掉了mongodb进程,如下
Out of memory: Kill process 5372 (mongod) score 130 or sacrifice child
Killed process 5372 (mongod), UID 0, total-vm:2539052kB, anon-rss:2117096kB, file-rss:0kB, shmem-rss:0kB

(转载过来的。一些命令不适用于centos7及以后的)
Linux有一个特性:OOM Killer,一个保护机制,用于避免在[内存]不足的时候不至于出现严重问题,把一些无关的进程优先杀掉,即在内存严重不足时,系统为了继续运转,内核会挑选一个进程,将其杀掉,以释放内存,缓解内存不足情况,不过这种保护是有限的,不能完全的保护进程的运行。

Linux 分配内存策略
Linux内核根据应用程序的要求来分配内存,由于进程实际上并不会将分配的内存全部使用,所以,为了提高性能,内核采用了一种过度分配内存(over-commit-memory)的策略,来间接利用进程的空闲内存,提高内存的使用效率。一般来说,这没问题。但如果大多数进程都耗光自己的内存,就有麻烦了。因此此时,所有应用程序的内存之和大于物理内存。所以,必须杀掉一部分进程,一般来说,是选内存占用最大的进程杀掉。

挑选原理
挑选的过程由linux/mm/oom_kill.c里的 oom_badness() 函数决定,挑选的算法很直接:是那个最占用内存的进程。
/**

  • 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;
      }
      避免被杀掉的办法
      从上面的代码里可以看到 oom_badness() 给每个进程打分,根据 points 的高低来决定杀哪个进程,分数越低越不会被杀掉。
      这个 points 可以根据 adj 调节,root 权限的进程通常被认为很重要,不应该被轻易杀掉,所以打分的时候可以得到 3% 的优惠(adj -= 30; 分数越低越不容易被杀掉)。
      我们可以在用户空间通过操作每个进程的 oom_adj 内核参数来使得进程不容易被 OOM killer 选中杀掉。比如,如果不想 test进程被轻易杀掉的话可以找到 test运行的进程号后,调整 oom_score_adj 为 -15(注意 points 越小越不容易被杀):

ps aux | grep test

test 2334 1.6 2.1 623800 4876 ? Ssl 09:52 0:00 /usr/sbin/test

cat /proc/2334/oom_score_adj

0

echo -15 > /proc/2334/oom_score_adj

当然,也可以完全关闭 OOM killer,但线上生产环境最好不要这么做。

OOM_killer是Linux自我保护的方式,当内存不足时不至于出现太严重问题,有点壮士断腕的意味

在kernel 2.6,内存不足将唤醒oom_killer,挑出/proc//oom_score最大者并将之kill掉

为了保护重要进程不被oom-killer掉,我们可以:echo -17 > /proc//oom_adj,-17表示禁用OOM

我们也可以对把整个系统的OOM给禁用掉:

sysctl -w vm.panic_on_oom=1 (默认为0,表示开启)

sysctl -p

值得注意的是,有些时候 free -m 时还有剩余内存,但还是会触发OOM-killer,可能是因为进程占用了特殊内存地址

平时我们应该留意下新进来的进程内存使用量,免得系统重要的业务进程被无辜牵连

可用 top M 查看最消耗内存的进程,但也不是进程一超过就会触发oom_killer

参数/proc/sys/vm/overcommit_memory可以控制进程对内存过量使用的应对策略

当overcommit_memory=0 允许进程轻微过量使用内存,但对于大量过载请求则不允许(默认)

当overcommit_memory=1 永远允许进程overcommit

当overcommit_memory=2 永远禁止overcommit

参考:
https://blog.csdn.net/pansaky/article/details/87518586
https://www.cnblogs.com/shenlinken/p/10987895.html
https://blog.csdn.net/weixin_34186128/article/details/94673229
https://www.cnblogs.com/yanqingxu/p/8316359.html

你可能感兴趣的:(Out of memory: Kill process 解决)