环境: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/<pid>/oom_score最大者并将之kill掉
为了保护重要进程不被oom-killer掉,我们可以:echo -17 > /proc/<pid>/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