文章目录
- Java内存溢出故障案例及Linux内存机制探究
- OOM Killer触发机制分析
- 如何避免系统触发OOM Killer
这部分内容属于demo案例分享,解决线上运维问题,思路是最重要的
Java内存溢出故障案例及Linux内存机制探究
这是一个线上数据分析应用故障案例,电话告警说分析任务失败,此数据分析程序是基于Java的,运行在一台CentOS6.x版本的操作系统上。作为一个经验丰富的运维老手,第1步是尝试是否能够远程SSH登录到系统,还好,SSH还能够远程连接。笔者首先怀疑是应用程序本身的问题,因为它在崩溃之前一点异常也没有。于是笔者查看了应用程序日志,没有错误,没有警告,也没有任何可疑的信息。看来问题好像不在程序代码上,那么就尝试从操作系统方面看看是否有可疑日志信息。果然在5min后,笔者通过执行dmesg命令的时候,发现了如下异常信息:
此日志对排查问题非常有用,从这里可以发现其实是Linux内核的问题,是OOM Killer(Out of memory Killer)kill了PID为30328的Java进程,而这个进程刚好是线上数据分析应用对应的进程。那么OOM Killer为什么要kill掉Java进程呢,这个要详细分析一下。
此日志对排查问题非常有用,从这里可以发现其实是Linux内核的问题,是OOM Killer(Out of memory Killer)kill了PID为30328的Java进程,而这个进程刚好是线上数据分析应用对应的进程。那么OOM Killer为什么要kill掉Java进程呢,这个要详细分析一下。要解决问题,找到关键的日志非常重要,上面第1步已经找到了异常日志,那么下面就来分析一下这些日志中都包含了哪些信息,理解了这些日志的含义,处理问题的方法也就很容易找到了。首先看到有个Out of memory关键字,很明显,这是内存溢出的标志,这通常是因为某时刻应用程序大量请求内存导致系统内存不足造成的,继而触发了Linux内核里的内存不足终结者Out of Memory(OOM)killer的内建机制。在内存过低的情况下,这个终结者会被激活,然后挑选出一个进程去终结掉,以腾出内存留给系统用,不至于让系统立刻崩溃。其实,从日志中也可以看出,是内存不够了,可以看到有Free swap=0kB的字样,另外还有oom_adj、oom_score_adj等值也均为0,这些值的含义,下面会做详细介绍。
最后,从日志中可知,此系统环境是2.6.32-431.el6.x86_64,其实是CentOS6.5x86_64版本的系统。先看看系统内存状态,从free看,可用物理内存很低,cached和buffers都比较低:
再观察系统message日志,跟dmesg命令获取的信息完全吻合
到这里为止,基本可以确定是系统内存不足,导致出现了这次故障,但是,为什么系统内存突然会占用完呢?为什么OOM Killer要kill掉这个数据分析的Java进程而不是其他进程?
OOM Killer触发机制分析
OOM Killer是Linux内核在内存不足情况下的一种管理机制,当内核检测到系统物理内存不足时,就会通过OOM Killer机制kill掉一些进程。kill进程的原则是通过使用一套启发式算法计算所有进程的分数,然后选出分数最高的那个进程kill掉。一般分数最高的进程占用的内存刚好是最大的。那么为什么会突然出现内存不足的情况呢?这就要说说进程与内存的运行机制。默认情况下,Linux内核根据应用程序的要求分配内存,通常来说应用程序分配了内存但是实际上并没有全部使用,为了提高性能,这部分没用的内存可以留作他用。这部分内存是属于每个进程的,内核直接回收利用的话比较麻烦,所以内核采用一种过度分配内存(over-commit memory)的办法来间接利用这部分“空闲”的内存,提高整体内存的使用效率。这个问题最类似的就是宽带运营商了,如现在电信宽带承诺卖给用户的都是300MB光纤带宽,而这实际上远远超出了他们的网络容量。他们赌的就是用户实际上并不会同时使用并用完分配的带宽上限。
一般来说这样做没有问题,但当大多数应用程序都消耗完自己的内存的时候麻烦就来了,如果这些应用程序的内存需求加起来超出了物理内存(包括Swap)的容量,这就会导致系统可用内存迅速降低甚至不够用的情况,也就是没有内存页能够再分配给进程了。此时,内核必须kill掉一些进程才能腾出内存空间保障系统正常运行,于是,OOM Killer机制被激活了,并通过启发式算法找出了要终结的进程。
理解了这个机制之后,就很容易理解了为什么Java进程“躺着也能中枪”了,因为它在系统上一般占用内存最多,所以如果发生Out of Memeory(OOM)的话,Java进程总是不幸第1个被kill掉的应用。OOM Killer机制也是可配置的,可以通过一些内核参数来调整OOM Killer的行为,避免系统在那里不停地kill进程。kill进程是根据每个进程打分的高低来决定的,root权限的进程通常被认为很重要,不应该被轻易kill掉,所以打分的时候可以得到3%的优势。运维人员可以在用户空间通过操作每个进程的oom_adj内核参数来降低哪些进程被OOM Killer选中kill掉的概率。例如,如果不想Java进程被轻易kill掉的话,可以找到Java运行的进程号后,调整oom_score_adj的值即可
如何避免系统触发OOM Killer
通过上面的分析解读已经知道发生了什么问题,但还是搞不清楚到底是谁触发了OOM Killer。通过进一步的了解和分析,找到了答案:这个数据分析应用系统,每天定时分析的数据量在9000万左右,而故障的当天,要分析的数据量在5亿左右,数据量的陡增,导致分析程序占用大量内存,进而出现内存资源不足,所以数据量增大是导致触发OOM Killer的直接原因。
此外,查看系统/proc/sys/vm/overcommit_memory的设置
cat /proc/sys/vm/overcommit_memory
此值为1,表示用户申请内存的时候,系统不会对内存是否够用进行检查,这样,就会出现内存超过可用内存的情况,进而触发OOM Killer,这也是此次事故的另一个原因。要尽量避免OOM Killer的产生,可以设置/proc/sys/vm/overcommit_memory为0,这样,可以最大限度地避免系统触发OOM Killer。
end…