OOM Killer

宕机判断与预处理

OOM Killer原理

  • 当物理内存和交换空间爆满时,OOM Killer会计算每个进程的oom_score并选择杀死进程。oom_score的值越大就越有可能被 OOM Killer 选中。这里列举两个主要因素:

进程消耗的内存越大,oom_score越大
进程运行消耗的CPU时间越小,oom_score越大

  • Windows下没有OOM Killer机制,用户只能强制杀死进程或者重启机器。

内存溢出OOM判断和dump获取

内存溢出的导致无响应的主要原因,是由于JVM的内存全部耗光,但是依然需要进一步分配内存。此时JVM就会进行很多次的的Full GC,希望回收不使用的内存。但是由于数据都在使用,因此一直释放不掉内存,于是就一遍遍的进行Full GC。

按照以下办法判断是因为内存溢出导致的宕机

  • 利用top命令发现进程的CPU使用率非常高。
  • CPU高并不一定是在做Full GC,需要进一步确认。利用jstat -gc pid 2000 2000 这一命令。如下代码块所示,关注FGC一栏,如果数值增加很快,而且FGCT(Full GC 的总时间)数值很大,例如FGC 为20 FGCT已经2000以上,平均一次FGC耗时100s。这里就可以确定是内存溢出了导致的宕机,需要获取dump
  • 满足上面条件才认为是内存溢出,否则不在这个内存溢出处理范畴内。
  • 优先通过配置来让JVM自己生产dump,但是如果没有配置的话那就是手动获取。执行“ jmap -dump:format=b,live,file=路径 进程pid”,例如 代码块中获取22374进程的dump。
  • 如果客户环境没有配置JVM进行dump的参数,需要加上以防万一。配置宕机文件自动生成

系统的OOM Killer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
执行下面三个命令
egrep -i 'killed process' /var/log/messages
egrep -i 'killed process' /var/log/dmesg
egrep -i -r 'killed process' /var/log

如果有下列相关信息
"Out of memory: Kill process 31201 (java) score 783 or sacrifice child"
"Killed process 13090 (java)"
可以判断发生了OOM Killer,但是还需要通过内存占用和时间来确认与消失的BI进程是否吻合


例如
$ egrep -i -r 'killed process' /var/log
/var/log/messages-20190421:Apr 19 00:20:37 FineBI6 kernel: Killed process 30353 (abrt-hook-ccpp) total-vm:49637864kB, anon-rss:31433096kB, file-rss:32kB, shmem-rss:0kB


有的系统输出的时间戳,利用下面的工具来转换
https://tool.lu/timestamp/

测试方法

1
2
3
4
// oom_adj=15时,必然会被OOM Killer选中
echo 15 > /proc/[PID]/oom_adj
// 手动触发OOM Killer
echo f > /proc/sysrq-trigger

监测宕机 (信号量 + JVM Shutdown Hook)

  • 信号量
    利用sun.misc.SignalHandler类,可以监听除了kill -9之外的其他关闭信号。Java对每个信号量都启动一个线程进行处理,注册信号量后会启动”SIGTERM handler” 线程。即便主线程被阻塞,信号依然可以得到处理。

  • JVM Shutdown Hook
    当程序执行System.exit(0)退出时,信号量方法是无法知道的,这时候需要使用JVM Shutdown Hook。