前言
很早都想写这篇文章了,一直迫于时间上的约束,拖到了今天。
这件事是真实的发送在我们的生产环境上,其中的一台服务器上跑着 4 个 jar 程序,隔三差五的会发送进程突然消失的问题。
虽然,这个服务器上跑的 java 程序不是特别的重要,但是作为一个程序员,我们不能让程序死的不明不白,必须找到其中的原因。本文便是记录一下这个问题产生的原因。
排查
当一个 Java 进程突然消失后,你会如何进行排查?从哪里开始入手?
遇到这个问题,相信没经验的人肯定是干着急。但是干着急是没用的,必须要找证据。我首先安排运维排查,但是运维也从来没遇到过这个棘手的问题。于是,只能我亲自上阵了。
我首先看了看 history,看看是不是有人在服务器上操作了什么,结果 history 里什么也没有。
于是我又去查看进程所在目录,看看是不是因为 OOM 导致的,如果是 OOM,那么通过我们配置的参数,程序肯定会留下 dump 文件。但是,经过我的排查发现,消失的进程并没有留下 dump 文件。
另外在排查过程中,也使用了一堆命令,比如 top、free -h、ps、df 等各项指标均没有明显异常。
于是,我初步得出结论,可能是系统原因造成的进程消失。
journalctl -k | grep -i -e memory -e oom
执行上面的命令,可以初步排查出,消失的进程是否是触发了 Linux 系统的 OOMKiller。
或者通过查看、检查相关的日志文件(/var/log/messages)。如果出现类似下面的 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 tainted 2.6.32-279.1.1.el6.i686 #1
...
21556 total pagecache pages
21049 pages in swap cache
Swap cache stats: add 12819103, delete 12798054, find 3188096/4634617
Free swap = 0kB
Total swap = 524280kB
131071 pages RAM
0 pages HighMem
3673 pages reserved
67960 pages shared
124940 pages non-shared
这些日志信息显示对应进程触发 Linux 内核里的 Out of Memory (OOM) killer。
产生的原因通常是因为某时刻应用程序大量请求内存导致系统内存不足造成的,这会触发 OOM killer,进而会杀掉某个进程以腾出内存留给系统用,不致于让系统立刻崩溃。
OOM killer
Linux 内核根据应用程序的要求分配内存,通常来说应用程序分配了内存但是并没有实际全部使用,为了提高性能,这部分没用的内存可以留作它用,这部分内存是属于每个进程的,内核直接回收利用的话比较麻烦,所以内核采用一种过度分配内存(over-commit memory)的办法来间接利用这部分 “空闲” 的内存,提高整体内存的使用效率。一般来说这样做没有问题,但当大多数应用程序都消耗完自己的内存的时候麻烦就来了,因为这些应用程序的内存需求加起来超出了物理内存(包括 swap)的容量,内核(OOM killer)必须杀掉一些进程才能腾出空间保障系统正常运行。用银行的例子来讲可能更容易懂一些,部分人取钱的时候银行不怕,银行有足够的存款应付,当全国人民(或者绝大多数)都取钱而且每个人都想把自己钱取完的时候银行的麻烦就来了,银行实际上是没有这么多钱给大家取的。
内核检测到系统内存不足、挑选并杀掉某个进程的过程可以参考内核源代码 linux/mm/oom_kill.c,当系统内存不足的时候,out_of_memory() 被触发,然后调用 select_bad_process() 选择一个 “bad” 进程杀掉,如何判断和选择一个 “bad” 进程呢,总不能随机选吧?挑选的过程由 oom_badness() 决定,挑选的算法和想法都很简单很朴实:最 bad 的那个进程就是那个最占用内存的进程。
配置解决 OOM killer
解决这个问题最简单的办法就是增加内存;其二就是优化程序占用的内存;当然还有一种临时的办法就是调整内核参数,让 Java 进程不容易被 OOM killer 发现。
为了保护重要进程不被 oom-killer 掉,我们可以:echo -17 > /proc//oom_adj。其中的数字 -17 表示禁用 OOM。
或者把整个系统的 OOM 给禁用掉:
sysctl -w vm.panic_on_oom=1sysctl -p
也或者通过参数 /proc/sys/vm/overcommit_memory 控制进程对内存过量使用的应对策略。
- 当 overcommit_memory=0 允许进程轻微过量使用内存,但对于大量过载请求则不允许
- 当 overcommit_memory=1 永远允许进程 overcommit
- 当 overcommit_memory=2 永远禁止 overcommit
以上内容,希望能给大家带来收获。如果你遇到过类似问题,可以在评论区分享一下你的经验,如果没有遇到过,知道这个问题点就可以了,以后再遇到类似问题就不至于慌了阵脚!
: » 记一次生产服务器Java进程突然消失问题排查!
原创文章,作者:kepupublish,如若转载,请注明出处:https://blog.ytso.com/252258.html