如何理解Linux中的OOMjava锁机制理解

用户名:老徐_kevin
文章数:140
评论数:566
访问量:459409
注册日期:
阅读量:1297
阅读量:3317
阅读量:585421
阅读量:471228
51CTO推荐博文
Linux&内核有个机制叫OOM&killer(Out-Of-Memory&killer),该机制会监控那些占用内存过大,尤其是瞬间很快消耗大量内存的进程,为了防止内存耗尽而内核会把该进程杀掉。典型的情况是:某天一台机器突然ssh远程登录不了,但能ping通,说明不是网络的故障,原因是sshd进程被OOM&killer杀掉了(多次遇到这样的假死状况)。重启机器后查看系统日志/var/log/messages会发现Out&of&Memory:&Kill&process&1865(sshd)类似的错误信息。防止重要的系统进程触发(OOM)机制而被杀死:可以设置参数/proc/PID/oom_adj为-17,可临时关闭linux内核的OOM机制。内核会通过特定的算法给每个进程计算一个分数来决定杀哪个进程,每个进程的oom分数可以/proc/PID/oom_score中找到。我们运维过程中保护的一般是sshd和一些管理agent。保护某个进程不被内核杀掉可以这样操作:echo&-17&&&/proc/$PID/oom_adj如何防止sshd被杀,可以这样操作:pgrep&-f&"/usr/sbin/sshd"&|&while&read&PID;do&echo&-17&&&/proc/$PID/oom_done可以在计划任务里加入这样一条定时任务,就更安全了:#/etc/cron.d/oom_disable
*/1****&root&pgrep&-f&"/usr/sbin/sshd"&|&while&read&PID;do&echo&-17&&&/proc/$PID/oom_done为了避免重启失效,可以写入/etc/rc.d/rc.localecho&-17&&&/proc/$(pidof&sshd)/oom_adj至于为什么用-17而不用其他数值(默认值为0),这个是由linux内核定义的,查看内核源码可知:以Linux3.3.6版本的Kernel源码为例,路径为linux-3.6.6/include/linux/oom.h,阅读内核源码可知oom_adj的可调值为15到-16,其中15最大-16最小,-17为禁止使用OOM。oom_score为2的n次方计算出来的,其中n就是进程的oom_adj值,所以oom_score的分数越高就越会被内核优先杀掉。当然还可以通过修改内核参数禁止OOM机制#&sysctl&-w&vm.panic_on_oom=1
vm.panic_on_oom&=&1&&&//1表示关闭,默认为0表示开启OOM
#&sysctl&-p为了验证OOM机制的效果,我们不妨做个测试。首先看看我系统现有内存大小,没错96G多,物理上还要比查看的值大一些。再看看目前进程最大的有哪些,top查看,我目前只跑了两个java程序的进程,分别4.6G,再往后redis进程吃了21m,iscsi服务占了32m,gdm占了25m,其它的进程都是几M而已。现在我自己用C写一个叫bigmem程序,我指定该程序分配内存85G#include&&stdio.h&
#include&&stdlib.h&
#include&&string.h&
#define&PAGE_SZ&(1&&12)
int&main()&{
&&&&int&i;
&&&&int&gb&=&85;&//以GB为单位分配内存大小
&&&&for&(i&=&0;&i&&&((unsigned&long)gb&&30)/PAGE_SZ&;&++i)&{
&&&&&&&&void&*m&=&malloc(PAGE_SZ);
&&&&&&&&if&(!m)
&&&&&&&&&&&&
&&&&&&&&memset(m,&0,&1);
&&&&printf("allocated&%lu&MB\n",&((unsigned&long)i*PAGE_SZ)&&20);
&&&&getchar();
&&&&return&0;
}呵呵,效果明显,然后执行后再用top查看,排在第一位的是我的bigmem,RES是物理内存,已经吃满了85G。继续观察,当bigmem稳定保持在85G一会后,内核会自动将其进程kill掉,增长的过程中没有被杀,如果不希望被杀可以执行pgrep&-f&"bigmem"&|&while&read&PID;&do&echo&-17&&&/proc/$PID/oom_done执行以上命令前后,明显会对比出效果,就可以体会到内核OOM机制的实际作用了。如果你觉得写C代码麻烦,我告诉大家另外一个最简单的测试触发OOM的方法,可以把某个进程的oom_adj设置到15(最大值),最容易触发。然后执行以下命令:echo&f&&&/proc/sysrq-trigger&&&//&'f'&-&Will&call&oom_kill&to&kill&a&memory&hog&process.以下我来触发mysqld的OOM看看:需要注意的是这个测试,只是模拟OOM,不会真正杀掉进程ps&-ef&|&grep&mysqld&|&grep&-v&grep查看mysql进程,发现依然存在注意:1.Kernel-2.6.26之前版本的oomkiller算法不够精确,RHEL&6.x版本的2.6.32可以解决这个问题。2.子进程会继承父进程的oom_adj。3.OOM不适合于解决内存泄漏(Memory&leak)的问题。4.有时free查看还有充足的内存,但还是会触发OOM,是因为该进程可能占用了特殊的内存地址空间。本文出自 “” 博客,谢绝转载!
了这篇文章
类别:┆阅读(0)┆评论(0)
11:23:52 14:50:30 15:01:39 15:03:07 15:59:08 16:12:57 21:28:36 21:39:56 22:48:59 22:49:10 22:55:40 22:58:21 23:25:49 23:27:53 00:00:48 15:50:35 16:33:08 16:58:00 15:21:10 12:35:35 18:59:02 21:32:42 21:58:04 22:15:22 22:25:30 &&1&
&&页数 ( 1/2 ) &博客访问: 2959594
博文数量: 253
博客积分: 5347
博客等级: 大校
技术积分: 13680
注册时间:
认证徽章:
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: LINUX
最近在工作中遇到下面的问题:
&&&&active_anon:16777 inactive_anon:13946 isolated_anon:0
&active_file:14 inactive_file:37 isolated_file:0
&unevictable:0 dirty:0 writeback:0 unstable:0
&free:2081 slab_reclaimable:299 slab_unreclaimable:26435
&mapped:53 shmem:171 pagetables:289 bounce:0
Normal free:8324kB min:2036kB low:2544kB high:3052kB active_anon:67108kB inactive_anon:55784kB active_file:56kB inactive_file:148kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:260096kB mlocked:0kB dirty:0kB writeback:0kB mapped:212kB shmem:684kB slab_reclaimable:1196kB slab_unreclaimable:105740kB kernel_stack:648kB pagetables:1156kB unstable:0kB bounce:0kB writeback_tmp:0kB pages_scanned:0 all_unreclaimable? no
lowmem_reserve[]: 0 0
Normal: 655*4kB 663*8kB 17*16kB 4*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 8324kB
222 total pagecache pages
0 pages in swap cache
Swap cache stats: add 0, delete 0, find 0/0
Free swap &= 0kB
Total swap = 0kB
65536 pages of RAM
2982 free pages
3251 reserved pages
26734 slab pages
709 pages shared
0 pages swap cached
Out of memory: kill process 6184 (XXX) score 166 or a child
Killed process 6184 (XXX)
&&&&&& 从上面的打印信息可以看出,内存不足触发了Linux内核的OOM机制&&&
&Linux 下有个特性叫作
OOM killer(Out of Memory),从字面的意思可以看出和内存溢出相关,当内存耗尽时,该问题就会出现。在Linux2.6.内核中,当该功能打开后,在内存耗尽时,会根据一定的值计算出一个合适的用户空间的进程给kill掉,以便释放更多的内存,保证整个系统的稳定运行。在系统的日志中通常会有下面的打印日志:Out of memory: kill process 959 (sshd) score 55 or a child。&
OOM什么时候出现?
我们在用户空间申请内存时,一般使用的是malloc,是不是当malloc返回为空时,没有可以申请的内存空间了就会返回呢?答案是否定的。在关于malloc的申请内存的机制中有下面的一段描述:
default, Linux follows an optimistic memory allocation strategy. This means
that when malloc() returns non-NULL there is no guarantee that the memory
really is available. This is a really bad bug. In case it turns out that the
system is out of memory, one or more processes will be killed by the infamous
OOM killer. In case Linux is employed under circumstances where it would be
less desirable to suddenly lose some randomly picked processes, and moreover
the kernel version is sufficiently recent, one can switch off this&overcommitting behavior using a command like:
上面的描述中说明了在Linux中当malloc返回的是非空时,并不代表有可以使用的内存空间。Linux系统允许程序申请比系统可用内存更多的内存空间,这个特性叫做overcommit特性,这样做可能是为了系统的优化,因为不是所有的程序申请了内存就会立刻使用,当真正的使用时,系统可能已经回收了一下内存。但是,当你使用时Linux系统没有内存可以使用时,OOM
Killer就会出来让一些进程退出。
&Linux下有3种Overcommit的策略(参考内核文档:vm/overcommit-accounting),可以在/proc/sys/vm/overcommit_memory配置(取0,1和2三个值,默认是0)。&
(1)0:启发式策略,比较严重的Overcommit将不能得逞,比如你突然申请了128TB的内存。而轻微的overcommit将被允许。另外,root能Overcommit的值比普通用户要稍微多
(2)永远允许overcommit,这种策略适合那些不能承受内存分配失败的应用,比如某些科学计算应用。&
(3)永远禁止overcommit,在这个情况下,系统所能分配的内存不会超过swap+RAM*系数(/proc/sys/vm/overcmmit_ratio,默认50%,你可以调整),如果这么多资源已经用光,那么后面任何尝试申请内存的行为都会返回错误,这通常意味着此时没法运行任何新程序。
/proc/sys/vm
# cat overcommit_ratio
当然我可以修改proc//oom_adj的值,这里的默认值为0,当我们设置为-17时,对于该进程来说,就不会触发OOM机制,被杀掉。
echo -17 >
/proc/$(pidof sshd)/oom_adj
这里为什么是-17呢?这和Linux的实现有关系。在Linux内核中的oom.h文件中,可以看到下面的定义:
/proc//oom_adj set to -17 protects from the oom-killer */
OOM_DISABLE (-17)
inclusive */
OOM_ADJUST_MIN (-16)
OOM_ADJUST_MAX 15
这个oom_adj中的变量的范围为15到-16之间。越大越容易被kill。oom_score就是它计算出来的一个值,就是根据这个值来选择哪些进程被kill掉的。
总之,通过上面的分析可知,满足下面的条件后,就是启动OOM机制。
1) VM里面分配不出更多的page(注意linux
kernel是延迟分配page策略,及用到的时候才alloc;所以malloc
+ memset才有效)。
2) 用户地址空间不足,这种情况在32bit机器上及user
space超过了3GB,在64bit机器上不太可能发生。
2 & & 当该机制被触发后,会让什么样的进程退出?
只要存在overcommit,就可能会有OOM
killer。 Linux系统的选择策略也一直在不断的演化。我们可以通过设置一些值来影响OOM
killer做出决策。Linux下每个进程都有个OOM权重,在/proc//oom_adj里面,取值是-17到+15,取值越高,越容易被干掉。& 最终OOM
killer是通过/proc//oom_score这个值来决定哪个进程被干掉的。这个值是系统综合进程的内存消耗量、CPU时间(utime
+ stime)、存活时间(uptime - start time)和oom_adj计算出的,消耗内存越多分越高,存活时间越长分越低。总之,总的策略是:损失最少的工作,释放最大的内存同时不伤及无辜的用了很大内存的进程,并且杀掉的进程数尽量少。& 另外,Linux在计算进程的内存消耗的时候,会将子进程所耗内存的一半同时算到父进程中。
3. & & & 在这里我们看一下内核是怎么实现的?
&& 下面的流程图是out_of_memory的调用关系,
__out_of_memory函数主要做了两件事,1.
调用select_bad_process函数选择一个最优的进程杀掉,2.
根据选择的最优的进程,调用函数oom_kill_process,杀掉该进程。
static void __out_of_memory(gfp_t gfp_mask, int order)
&&&&struct task_struct *p;
&&&&unsigned long points;
//如果sysctl_oom_kill_allocating_task值设置了,就会直接杀掉申请内存的进程。
&&&&if (sysctl_oom_kill_allocating_task)
&&&&&&&&if (!oom_kill_process(current, gfp_mask, order, 0, NULL,
&&&&&&&&&&&&&&&&"Out of memory (oom_kill_allocating_task)"))
&&&&&&&&&&&&return;
&&&& * Rambo mode: Shoot down a process and hope it solves whatever
&&&& * issues we may have.
&&&&p = select_bad_process(&points, NULL);
&&&&if (PTR_ERR(p) == -1UL)
&&&&&&&&return;
&&&&/* Found nothing?!?! Either we hang forever, or we panic. */
&&&&if (!p) {
&&&&&&&&read_unlock(&tasklist_lock);
&&&&&&&&panic("Out of memory and no killable processes...\n");
&&&&if (oom_kill_process(p, gfp_mask, order, points, NULL,
&&&&&&&&&&&& "Out of memory"))
&&&&&&&&goto retry;
select_bad_process函数主要是对变量所有的进程,并对一些不符合要求的进程进行过滤,然后调用badness函数,选择一个最优的进程,然后杀掉。
struct task_struct *select_bad_process(unsigned long *ppoints,struct mem_cgroup *mem)
&&&&struct task_struct *p;
&&&&struct task_struct *chosen = NULL;
&&&&struct timespec uptime;
&&&&*ppoints = 0;
&&&&do_posix_clock_monotonic_gettime(&uptime);
&&&&for_each_process(p) {//遍历所有的进程包括用户进程和内核进程
&&&&&&&&unsigned long points;
&&&&&&&&/*
&&&&&&&& * skip kernel threads and tasks which have already released
&&&&&&&& * their mm. 跳过内核进程
&&&&&&&& */
&&&&&&&&if (!p->mm)
&&&&&&&&&&&&continue;
&&&&&&&&/* skip the init task
跳过Init进程*/
&&&&&&&&if (is_global_init(p))
&&&&&&&&&&&&continue;
&&&&&&&&if (mem && !task_in_mem_cgroup(p, mem))
&&&&&&&&&&&&continue;
&&&&&&&&if (test_tsk_thread_flag(p, TIF_MEMDIE))
&&&&&&&&&&&&return ERR_PTR(-1UL);
&&&&&&&&if (p->flags & PF_EXITING) {
&&&&&&&&&&&&if (p != current)
&&&&&&&&&&&&&&&&return ERR_PTR(-1UL);
&&&&&&&&&&&&chosen = p;
&&&&&&&&&&&&*ppoints = ULONG_MAX;
//这里就是 #define OOM_DISABLE (-17) 也就是/proc/<pid>/oom_adj这个值
&&&&&&&&if (p->signal->oom_adj == OOM_DISABLE)
&&&&&&&&&&&&continue;
//对其它的进程调用badness()函数来计算相应的score,score最高的将被选中
&&&&&&&&points = badness(p, uptime.tv_sec);
&&&&&&&&if (points > *ppoints || !chosen) {
&&&&&&&&&&&&chosen = p;
&&&&&&&&&&&&*ppoints = points;
&&&&return chosen;
函数badness()就是根据各种条件进行判断,找到一个最应该杀死的进程。主要的选择条件是下面的几点:
(1)score初始值为该进程占用的total_vm;
(2)如果该进程有子进程,子进程独自占用的total_vm/2加到本进程score;
(3)score随着该进程的cpu_time以及run_time的增长而减少,也就是运行的时间越长,被kill掉的几率越小
nice大于0的进程,score*2;
(5)对于拥有超级权限的进程,或者直接磁盘交互的进程降低score;
(6)如果和current进程在内存上没有交集,则该进程降低score;
(7)最后根据该进程的oom_adj,计算得出最终的score;
unsigned long badness(struct task_struct *p, unsigned long uptime)
&&&&unsigned long points, cpu_time, run_time;
&&&&struct mm_struct *mm;
&&&&struct task_struct *child;
&&&&int oom_adj = p->signal->oom_adj;
&&&&struct task_cputime task_time;
&&&&unsigned long utime;
&&&&unsigned long stime;
&&&//如果OOM是被禁止的,则直接返回。
&&&&if (oom_adj == OOM_DISABLE)
&&&&&&&&return 0;
&&&&task_lock(p);
&&&&mm = p->mm;
&&&&if (!mm) {
&&&&&&&&task_unlock(p);
&&&&&&&&return 0;
&&&& * The memory size of the process is the basis for the badness.
&&&&&&该进程占用的内存大小
&&&&points = mm->total_vm;
&&&& * After this unlock we can no longer dereference local variable `mm'
&&&&task_unlock(p);
&&&& * swapoff can easily use up all memory, so kill those first.
&&&&if (p->flags & PF_OOM_ORIGIN)
&&&&&&&&return ULONG_MAX;
&&&&list_for_each_entry(child, &p->children, sibling) {
&&&&&&&&task_lock(child);
&//如果该进程含有子进程,该进程子进程total_vm的一半加入到points中
&&&&&&&&if (child->mm != mm && child->mm)
&&&&&&&&&&&&points += child->mm->total_vm/2 + 1;
&&&&&&&&task_unlock(child);
&&&& * CPU time is in tens of seconds and run time is in thousands
&&&&&&&&&* of seconds. There is no particular reason for this other than
&&&&&&&&&* that it turned out to work very well in practice.
&&&&thread_group_cputime(p, &task_time);
&&&&utime = cputime_to_jiffies(task_time.utime);
&&&&stime = cputime_to_jiffies(task_time.stime);
&&&&cpu_time = (utime + stime) >> (SHIFT_HZ + 3);
&&&&if (uptime >= p->start_time.tv_sec)
&&&&&&&&run_time = (uptime - p->start_time.tv_sec) >> 10;
&&&&&&&&run_time = 0;
// score和进程的cpu_time以及run_time成反比,也就是该进程运行的时间越长,score值越低。
&&&&if (cpu_time)
&&&&&&&&points /= int_sqrt(cpu_time);
&&&&if (run_time)
&&&&&&&&points /= int_sqrt(int_sqrt(run_time));
&&&& * Niced processes are most likely less important, so double
&&&& * their badness points. nice大于0的进程,score翻倍,nice的范围一般是-20~+19,值越大优先级越低。
&&&&if (task_nice(p) > 0)
&&&&&&&&points *= 2;
&&&& * Superuser processes are usually more important, so we make it
&&&& * less likely that we kill those. 对设置了超级权限的进程降低score,具有超级权限的进程更加重要。
&&&&if (has_capability_noaudit(p, CAP_SYS_ADMIN) ||
has_capability_noaudit(p, CAP_SYS_RESOURCE))
&&&&&&&&points /= 4;
&&&& * We don't want to kill a process with direct hardware access.
&&&& * Not only could that mess up the hardware, but usually users
&&&& * tend to only have this flag set on applications they think
&&&& * of as important. 对设置了超级权限的进程降低score
&&&&if (has_capability_noaudit(p, CAP_SYS_RAWIO))
&&&&&&&&points /= 4;
&&&& * If p's nodes don't overlap ours, it may still help to kill p
&&&& * because p may have allocated or otherwise mapped memory on
&&&& * this node before. However it will be less likely.
如果和p进程在内存上没有交集的进程降低score
&&&&if (!has_intersects_mems_allowed(p))
&&&&&&&&points /= 8;
&&&& * Adjust the score by oom_adj.
最后是根据该进程的oom_adj进行移位操作,计算最终的score,这样根据各个策略就计算出来scope值,该值越大,进程被杀死的概率也就越高
&&&&if (oom_adj) {
&&&&&&&&if (oom_adj > 0) {
&&&&&&&&&&&&if (!points)
&&&&&&&&&&&&&&&&points = 1;
&&&&&&&&&&&&points <<= oom_adj;
&&&&&&&&} else
&&&&&&&&&&&&points >>= -(oom_adj);
#ifdef DEBUG
&&&&printk(KERN_DEBUG "OOMkill: task %d (%s) got %lu points\n",
&&&&p->pid, p->comm, points);
&&&&return points;
在选择完那个进程后,调用下面的程序,发送SIGKILL信号杀死该进程,相当于用户只需Kill
-9 pid 杀死进程。
static void __oom_kill_task(struct task_struct *p, int verbose)
&&&&if (is_global_init(p)) {
&&&&&&&&WARN_ON(1);
&&&&&&&&printk(KERN_WARNING "tried to kill init!\n");
&&&&&&&&return;
&&&&if (!p->mm) {
&&&&&&&&WARN_ON(1);
&&&&&&&&printk(KERN_WARNING "tried to kill an mm-less task!\n");
&&&&&&&&return;
&&&&if (verbose)
&&&&&&&&printk(KERN_ERR "Killed process %d (%s)\n",
&&&&&&&&&&&&&&&&task_pid_nr(p), p->comm);
&&&&p->rt.time_slice = HZ;
&&&&set_tsk_thread_flag(p, TIF_MEMDIE);
&&&&force_sig(SIGKILL, p);
参考文献:
阅读(18488) | 评论(0) | 转发(5) |
相关热门文章
给主人留下些什么吧!~~
请登录后评论。Linux内核OOM机制的详细分析 - CSDN博客
Linux内核OOM机制的详细分析
&&&&&&& Linux内核根据应用程序的要求分配内存,通常来说应用程序分配了内存但是并没有实际全部使用,为了提高性能,这部分没用的内存可以留作它用,这部分内存是属于每个进程的,内核直接回收利用的话比较麻烦,所以内核采用一种过度分配内存(over-commit memory)的办法来间接利用这部分“空闲”的内存,提高整体内存的使用效率。一般来说这样做没有问题,但当大多数应用程序都消耗完自己的内存的时候麻烦就来了,因为这些应用程序的内存需求加起来超出了物理内存(包括swap)的容量,内核(OOM
killer)必须杀掉一些进程才能腾出空间保障系统正常运行。用银行的例子来讲可能更容易懂一些,部分人取钱的时候银行不怕,银行有足够的存款应付,当全国人民(或者绝大多数)都取钱而且每个人都想把自己钱取完的时候银行的麻烦就来了,银行实际上是没有这么多钱给大家取的。
&&&&&&&比如某天一台机器突然ssh远程登录不了,但能ping通,说明不是网络的故障,原因是sshd进程被OOM killer杀掉了。重启机器后查看系统日志/var/log/messages会发现Out of Memory:Killprocess 1865(sshd)类&#20284;的错误信息。又比如有时VPS的MySQL总是无缘无故挂掉,或者VPS
经常死机,登陆到终端发现都是常见的 Out of memory 问题。这通常是因为某时刻应用程序大量请求内存导致系统内存不足造成的,这时会触发 Linux 内核里的 Out of Memory (OOM) killer,OOM killer 会杀掉某个进程以腾出内存留给系统用,不致于让系统立刻崩溃。如果检查相关的日志文件(/var/log/messages)就会看到下面类&#20284;的Out of memory:Kill process 信息:
& Out of memory: Kill process 9682(mysqld) score 9 or sacrifice child
& Killed process 9682, UID 27,(mysqld) total-vm:47388kB, anon-rss:3744kB, file-rss:80kB
& httpd invoked oom-killer:gfp_mask=0x201da, order=0, oom_adj=0, oom_score_adj=0
& httpd cpuset=/ mems_allowed=0
& Pid: 8911, comm: httpd Not tainted2.6.32-279.1.1.el6.i686 #1
& 21556 total pagecache pages
& 21049 pages in swap cache
& Swap cache stats: add ,delete , find 4617
& Free swap& = 0kB
& Total swap = 524280kB
&&131071 pages RAM
& 0 pages HighMem
& 3673 pages reserved
&&&67960 pages shared
& 124940 pages non-shared
&&&&&&& 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)
&&&&&&&&&&
&&&&&&&&&&
&&&&&&&&&& 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) &#43; atomic_long_read(&p-&mm-&nr_ptes)&#43;
&&&&&&&&&&&&&&&&&&&& &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 &#43;=
&&&&&&&&&& /*
&&&&&&&&&& &* 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 选中杀掉。比如,如果不想 MySQL 进程被轻易杀掉的话可以找到 MySQL 运行的进程号后,调整 /proc/PID/oom_score_adj 为 -15(注意 points越小越不容易被杀)防止重要的系统进程触发(OOM)机制而被杀死,内核会通过特定的算法给每个进程计算一个分数来决定杀哪个进程,每个进程的oom分数可以在/proc/PID/oom_score中找到。每个进程都有一个oom_score的属性,oom
killer会杀死oom_score较大的进程,当oom_score为0时禁止内核杀死该进程。设置/proc/PID/oom_adj可以改变oom_score,oom_adj的范围为【-17,15】,其中15最大-16最小,-17为禁止使用OOM,至于为什么用-17而不用其他数&#20540;(默认&#20540;为0),这个是由linux内核定义的,查看内核源码可知:路径为linux-xxxxx/include /uapi/linux/oom.h。
&&&&&& oom_score为2的n次方计算出来的,其中n就是进程的oom_adj&#20540;,oom_score的分数越高就越会被内核优先杀掉。当oom_adj=-17时,oom_score将变为0,所以可以设置参数/proc/PID/oom_adj为-17禁止内核杀死该进程。
&&&&&& 上面的那个MySQL例子可以如下解决来降低mysql的points,降低被杀掉的可能:
&&&&&&&&&&&&& # ps aux | grep mysqld
&&&&&&&&&&&&& mysql
2.1 76 ? Ssl 09:42 0:00 /usr/sbin/mysqld
&&&&&&&&&&&&& # cat /proc/2196/oom_score_adj
&&&&&&&&&&&&& 0
&&&&&&&&&&&&& # echo -15 & /proc/2196/oom_score_adj
&&&&&& 当然了,保证某个进程不被内核杀掉可以这样操作:
&&&&&&&&&&&&&& echo -17 & /proc/$PID/oom_adj
&&&&&& 例如防止sshd被杀,可以这样操作:
&&&&&&&&&&&&&& pgrep -f &/usr/sbin/sshd& | while read PID;do echo -17 & /proc/$PID/oom_done
&&&&&& 为了验证OOM机制的效果,我们不妨做个测试。
&&&&&& 首先看看我系统现有内存大小,没错96G多,物理上还要比查看的&#20540;大一些。
&&&&&& 再看看目前进程最大的有哪些,top查看,我目前只跑了两个java程序的进程,分别4.6G,再往后redis进程吃了21m,iscsi服务占了32m,gdm占了25m,其它的进程都是几M而已。
&&&&&& 现在我自己用C写一个叫bigmem程序,我指定该程序分配内存85G,呵呵,效果明显,然后执行后再用top查看,排在第一位的是我的bigmem,RES是物理内存,已经吃满了85G。
&&&&&& 继续观察,当bigmem稳定保持在85G一会后,内核会自动将其进程kill掉,增长的过程中没有被杀,如果不希望被杀可以执行
&&&&&& pgrep -f &bigmem& | while read PID; do echo -17 & /proc/$PID/oom_done
&&&&&& 执行以上命令前后,明显会对比出效果,就可以体会到内核OOM机制的实际作用了。
&&&&&&&注意,由任意调整的进程衍生的任意进程将继承该进程的 oom_score。例如:如果 sshd 进程不受 oom_killer 功能影响,所有由 SSH 会话产生的进程都将不受其影响。这可在出现 OOM 时影响 oom_killer 功能救援系统的能力。
&&&&&& 当然还可以通过修改内核参数禁止在内存出现OOM时采取杀掉进程的这种机制,但此时会触发kernel panic。当内存严重不足时,内核有两种选择:1.直接panic 2.杀掉部分进程,释放一些内存。通过/proc/sys/vm/panic_on_oom可以控制,当panic_on_oom为1时,直接panic,当panic_on_oom为0时内核将通过oom killer杀掉部分进程。(默认是为0的)
&&&&&&&&&&&&& # sysctl -w vm.panic_on_oom=1
&&&&&&&&&&&&& vm.panic_on_oom = 1 //1表示关闭,默认为0表示开启OOM killer
&&&&&&&&&&&&& # sysctl –p
&&&&&& 我们可以通过一些内核参数来调整 OOM killer 的行为,避免系统在那里不停的杀进程。比如我们可以在触发 OOM 后立刻触发 kernel panic,kernel panic 10秒后自动重启系统:
&&&&&&&&&&&&& # sysctl -w vm.panic_on_oom=1
&&&&&&&&&&&&& vm.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的问题(不过不推荐这样做):
&&&&&&&&&&&&& # sysctl -w vm.overcommit_memory=2
&&&&&&&&&&&&& # echo &vm.overcommit_memory=2& && /etc/sysctl.conf,
&&&&&& vm.overcommit_memory 表示内核在分配内存时候做检查的方式。这个变量可以取到0,1,2三个&#20540;。对取不同的&#20540;时的处理方式都定义在内核源码 mm/mmap.c 的 __vm_enough_memory 函数中。
&&&&&& 0:当用户空间请求更多的的内存时,内核尝试估算出剩余可用的内存。此时宏为 OVERCOMMIT_GUESS,内核计算:NR_FILE_PAGES 总量&#43;SWAP总量&#43;slab中可以释放的内存总量,如果申请空间超过此数&#20540;,则将此数&#20540;与空闲内存总量减掉 totalreserve_pages(?) 的总量相加。如果申请空间依然超过此数&#20540;,则分配失败。
&&&&&& 1:当设这个参数&#20540;为1时,宏为 OVERCOMMIT_ALWAYS,内核允许超量使用内存直到用完为止,主要用于科学计算。
&&&&&& 2:当设这个参数&#20540;为2时,此时宏为 OVERCOMMIT_NEVER,内核会使用一个决不过量使用内存的算法,即系统整个内存地址空间不能超过swap&#43;50%的RAM&#20540;,50%参数的设定是在overcommit_ratio中设定,内核计算:内存总量×vm.overcommit_ratio/100+SWAP 的总量,如果申请空间超过此数&#20540;,则分配失败。vm.overcommit_ratio 的默认&#20540;为50。
&&&&&& 以上为粗略描述,在实际计算时,如果非root进程,则在计算时候会保留3%的空间,而root进程则没有该限制。详细过程可看源码。
找出最有可能被 OOM Killer 杀掉的进程:
我们知道了在用户空间可以通过操作每个进程的 oom_adj 内核参数来调整进程的分数,这个分数也可以通过 oom_score 这个内核参数看到,比如查看进程号为981的 omm_score,这个分数被上面提到的 omm_score_adj 参数调整后(-15),就变成了3:
# cat /proc/981/oom_score
# echo -15 & /proc/981/oom_score_adj
# cat /proc/981/oom_score
下面这个 bash 脚本可用来打印当前系统上 oom_score 分数最高(最容易被 OOM Killer 杀掉)的进程:
#!/bin/bash
for proc in $(find /proc -maxdepth 1 -regex '/proc/[0-9]&#43;'); 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 &#43;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
1.Kernel-2.6.26之前版本的oomkiller算法不够精确,RHEL6.x版本的2.6.32可以解决这个问题。
2.子进程会继承父进程的oom_adj。
3.OOM不适合于解决内存泄漏(Memory leak)的问题。
4.有时free查看还有充足的内存,但还是会触发OOM,是因为该进程可能占用了特殊的内存地址空间。
参考:http://laoxu./7097
http://blog.chinaunix.net/uid--id-3063309.html
/Linux/2119.html
/2013/10/how-to-configure-the-linux-oom-killer/
/documentation/zh-CN/Red_Hat_Enterprise_Linux/6/html/Performance_Tuning_Guide/s-memory-captun.html
.cn/s/blog_012evk.html
http://skyou./461
http://c.biancheng.net/cpp/html/2827.html
附我的吃内存程序:
#include &stdlib.h&
#include &stdio.h&
int main(){
malloc(1);
oom_score为2的n次方计算出来的,其中n就是进程的oom_adj&#20540;,oom_score的分数越高就越会被内核优先杀掉。当oom_adj=-17时,oom_score将变为0,所以可以设置参数/proc/PID/oom_adj为-17禁止内核杀死该进程。
上面的那个MySQL例子可以如下解决来降低mysql的points,降低被杀掉的可能:
# ps aux | grep mysqld
mysql&&& &2.1 76 ?&&&&&&& Ssl& 09:42&&0:00 /usr/sbin/mysqld
# cat /proc/2196/oom_score_adj
# echo -15 & /proc/2196/oom_score_adj
当然了,保证某个进程不被内核杀掉可以这样操作:
&&& echo -17& /proc/$PID/oom_adj
例如防止sshd被杀,可以这样操作:
&&& pgrep-f&&/usr/sbin/sshd&&|&while&read PID;do&echo -17&& /proc/$PID/oom_done
为了验证OOM机制的效果,我们不妨做个测试。
首先看看我系统现有内存大小,没错96G多,物理上还要比查看的&#20540;大一些。
再看看目前进程最大的有哪些,top查看,我目前只跑了两个java程序的进程,分别4.6G,再往后redis进程吃了21m,iscsi服务占了32m,gdm占了25m,其它的进程都是几M而已。
现在我自己用C写一个叫bigmem程序,我指定该程序分配内存85G,呵呵,效果明显,然后执行后再用top查看,排在第一位的是我的bigmem,RES是物理内存,已经吃满了85G。
继续观察,当bigmem稳定保持在85G一会后,内核会自动将其进程kill掉,增长的过程中没有被杀,如果不希望被杀可以执行
pgrep -f&&bigmem&&|&while&read PID;&do&echo -17&& /proc/$PID/oom_done
执行以上命令前后,明显会对比出效果,就可以体会到内核OOM机制的实际作用了。
注意,由任意调整的进程衍生的任意进程将继承该进程的 oom_score。例如:如果 sshd 进程不受 oom_killer 功能影响,所有由 SSH 会话产生的进程都将不受其影响。这可在出现 OOM 时影响 oom_killer 功能救援系统的能力。
当然还可以通过修改内核参数禁止在内存出现OOM时采取杀掉进程的这种机制,但此时会触发kernel panic。当内存严重不足时,内核有两种选择:1.直接panic 2.杀掉部分进程,释放一些内存。通过/proc/sys/vm/panic_on_oom可以控制,当panic_on_oom为1时,直接panic,当panic_on_oom为0时内核将通过oomkiller杀掉部分进程。(默认是为0的)
# sysctl -wvm.panic_on_oom=1
vm.panic_on_oom =&1&&&//1表示关闭,默认为0表示开启OOM killer
# sysctl –p
我们可以通过一些内核参数来调整 OOM killer 的行为,避免系统在那里不停的杀进程。比如我们可以在触发 OOM 后立刻触发 kernel panic,kernel panic 10秒后自动重启系统:
# sysctl -w vm.panic_on_oom=1
vm.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的问题(不过不推荐这样做):
# sysctl -w vm.overcommit_memory=2
# echo &vm.overcommit_memory=2& && /etc/sysctl.conf,
vm.overcommit_memory表示内核在分配内存时候做检查的方式。这个变量可以取到0,1,2三个&#20540;。对取不同的&#20540;时的处理方式都定义在内核源码 mm/mmap.c 的 __vm_enough_memory 函数中。
0:当用户空间请求更多的的内存时,内核尝试估算出剩余可用的内存。此时宏为 OVERCOMMIT_GUESS,内核计算:NR_FILE_PAGES 总量&#43;SWAP总量&#43;slab中可以释放的内存总量,如果申请空间超过此数&#20540;,则将此数&#20540;与空闲内存总量减掉 totalreserve_pages(?) 的总量相加。如果申请空间依然超过此数&#20540;,则分配失败。
1:当设这个参数&#20540;为1时,宏为OVERCOMMIT_ALWAYS,内核允许超量使用内存直到用完为止,主要用于科学计算。
2:当设这个参数&#20540;为2时,此时宏为OVERCOMMIT_NEVER,内核会使用一个决不过量使用内存的算法,即系统整个内存地址空间不能超过swap&#43;50%的RAM&#20540;,50%参数的设定是在overcommit_ratio中设定,内核计算:内存总量×vm.overcommit_ratio/100+SWAP 的总量,如果申请空间超过此数&#20540;,则分配失败。vm.overcommit_ratio的默认&#20540;为50。
以上为粗略描述,在实际计算时,如果非root进程,则在计算时候会保留3%的空间,而root进程则没有该限制。详细过程可看源码。
找出最有可能被OOM Killer 杀掉的进程:
&&&&&& 我们知道了在用户空间可以通过操作每个进程的 oom_adj 内核参数来调整进程的分数,这个分数也可以通过 oom_score 这个内核参数看到,比如查看进程号为981的 omm_score,这个分数被上面提到的 omm_score_adj 参数调整后(-15),就变成了3:
&&&&&&&&&&&&& #cat /proc/981/oom_score
&&&&&&&&&&&&& 18
&&&&&&&&&&&&& # echo -15 &/proc/981/oom_score_adj
&&&&&&&&&&&&& # cat /proc/981/oom_score
&&&&&&&&&&&&& 3
&&&&&& 下面这个 bash 脚本可用来打印当前系统上 oom_score 分数最高(最容易被 OOM Killer 杀掉)的进程:
&&&&&&&&&&&&& #!/bin/bash
&&&&&&&&&&&&& for proc in $(find /proc -maxdepth1 -regex '/proc/[0-9]&#43;'); 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 &#43;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
1.Kernel-2.6.26之前版本的oomkiller算法不够精确,RHEL6.x版本的2.6.32可以解决这个问题。
2.子进程会继承父进程的oom_adj。
3.OOM不适合于解决内存泄漏(Memory leak)的问题。
4.有时free查看还有充足的内存,但还是会触发OOM,是因为该进程可能占用了特殊的内存地址空间。
参考:http://laoxu./7097
&&&&&&&&&& &&& & &http://blog.chinaunix.net/uid--id-3063309.html
& & & & & & & & & &/Linux/2119.html
& & & & & & & & & &/2013/10/how-to-configure-the-linux-oom-killer/
& & & & & & & & & &/documentation/zh-CN/Red_Hat_Enterprise_Linux/6/html/Performance_Tuning_Guide/s-memory-captun.html
& & & & & & & & & &.cn/s/blog_012evk.html
& & & &http://skyou./461
附一个吃内存程序:
#include &stdlib.h&
#include &stdio.h&
int main(){
&&&&&&&&&& while(1){
&&&&&&&&&&&&&&&&&&&& malloc(1);
&&&&&&&&&& }
&&&&&&&&&& return0;
&#65279;&#65279;
本文已收录于以下专栏:
相关文章推荐
Linux下面有个特性叫OOM killer(Out Of Memory killer),这个东西会在系统内存耗尽的情况下跳出来,选择性的干掉一些进程以求释放一些内存。典型的情况是:某天机器突然登不上...
from:&#160;/2013/10/how-to-configure-the-linux-oom-killer/
最近有位 VPS 客户抱怨 MySQL 无...
线上一些内存占用比较敏感的应用,在访问峰值的时候,偶尔会被kill掉,导致服务重启。发现是Linux的out-of-memory kiiler的机制触发的。
最近在处理一些lowmemorykiller相关的问题,于是对lowmemorykiller机制作了一个简单的了解。在这里总结一下。
首先,是lowmemorykiller的一些背景知识。
...
mkd-275 &#160;&#160;[000] d..3 : oom_score_adj_update: pid=22219 comm=ReferenceQueueD oom_score_ad...
一 : 前台进程 (Active Process): oom_adj为0。
前台进程包括 :
1、活动 正在前台接收用户输入
2、活动、服务与广播接收器正在执行一个on...
流程如下:
--& framework/base/core/jni/android_util_Process.cpp
android_os_Process_setOom...
1.[FAQ02683]如何将一个app 设置为持久app, 不被low memory kill 关闭
[Description]
如何将一个app 设置为常住app, 不被low memory ki...
前天线上出了个502错误,经排查是某开发人员写了条性感的程序吃掉了大部分内存,触发OOM-killer杀死Nginx
查看日志/var/log/messages发现类似&Out of Memor...
基本概念:
Linux 内核有个机制叫OOM killer(Out-Of-Memory killer),该机制会监控那些占用内存过大,尤其是瞬间很快消耗大量内存的进程,为了防止内存耗尽而内核会把该进程...
他的最新文章
讲师:何宇健
讲师:董岩
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)

我要回帖

更多关于 java中异常处理机制 的文章

 

随机推荐