这个问题通常由操作系统物理内存耗尽或应用运行的 Java 虚拟机进程 Crash 导致,本文以 Linux 操作系统为例说明如何解决。

操作系统物理内存耗尽,触发操作系统 OOM Killer

当操作系统物理内存和交换空间不够用时,操作系统的 OOM Killer 机制(默认打开)就会选择性地杀死进程,那么它是怎样知道要先杀死哪个进程呢?其实 Linux 的每个进程都有一个 oom_score(位于/proc/<pid>/oom_score),这个值越大,就越有可能被 OOM Killer 选中并杀死。

当一个进程被 OOM Killer 杀死以后会向操作系统日志写入杀死的进程 PID 等信息,所以判断进程是否被 OOM Killer ,就可以通过搜索操作系统日志来得知。

ECS 集群进程被 OOM Killer 的日志:

[Wed Aug 31 16:36:42 2017] Out of memory: Kill process 43805 (keystone-all) score 249 or sacrifice child
            [Wed Aug 31 16:36:42 2017] Killed process 43805 (keystone-all) total-vm:4446352kB, anon-rss:4053140kB, file-rss:68kB
            [Wed Aug 31 16:56:25 2017] keystone-all invoked oom-killer: gfp_mask=0x280da, order=0, oom_score_adj=0
            [Wed Aug 31 16:56:25 2017] keystone-all cpuset=/ mems_allowed=0
            [Wed Aug 31 16:56:25 2017] CPU: 2 PID: 88196 Comm: keystone-all Not tainted 3.10.0-327.13.1.el7.x86_64 #1

Swarm 集群进程被 oom-killer 的日志:

Memory cgroup out of memory: Kill process 20911 (beam.smp) score 1001 or sacrifice child
Killed process 20977 (sh) total-vm:4404kB, anon-rss:0kB, file-rss:508kB

总结起来搜索命令为:

root# grep -i 'killed process' /var/log/messages

或者:

root# egrep "oom-killer|total-vm" /var/log/messages

遇到此问题时,解决办法有以下几个:

  • 增大 ECS 实例物理内存或者减少被杀死的进程分配的内存值;
  • 检查 ECS 实例是否挂载了 Swap 交换分区,如果未挂载(阿里云 ECS 环境大多数 oom-killer 的问题都因为未挂载 Swap 交换分区,相比性能,进程健康性更加重要)请搜索 Linux 挂载 Swap 交换分区的方法并自行挂载 Swap 交换分区。

应用运行的 Java 虚拟机进程 Crash (异常退出)

Java 虚拟机通常会由于异常的 JNI 调用、C Heap OOM、其他 Bug 等原因在运行时进程 Crash,发生此问题时,会在当前 JVM 进程的工作目录(通过 pwdx <jvm_pid> 命令可查)生成一个 hs_err_<jvm_pid>.log。通常,从这个日志文件中即可查到 Java虚拟机 Crash 时执行的线程或原因(必要时需要通过允许生成 coredump 文件进一步分许)。

另外,在 EDAS Console 中可通过勾选应用基本信息页面的异常退出分析选项,配合应用监控告警功能,Java 虚拟机进程退出时,就会发出告警,然后登录 ECS 实例查找相关日志,分析具体原因。

EDAS应用异常退出分析