Linux 下的 OOM Killer理解Out of memory: Kill process

Tomcat服务进程无故被杀掉。
linux上tomcat服务器突然挂掉了,查看catalina.out没有发现什么错误信息。
查看/var/log/messages文件发现是因为内存不足系统杀死的
kernel: Out of memory: Kill process 15983 (java) score 149 or sacrifice child

出现OOM Killer的原因与解决方案

OOM Killer(Out of Memory Killer)是Linux内核在系统内存严重不足时,强行释放进程内存的一种机制。本文介绍Linux操作系统出现OOM Killer的可能原因。

问题现象

Linux操作系统在实例全局内存的内存不足时,会先触发内存回收机制释放内存,并将这部分被释放的内存分配给其他进程。如果内存回收机制不能处理系统内存不足的情况,则系统会触发OOM Killer强制释放进程占用的内存。触发OOM Killer的部分日志信息示例如下:

565 [六 9月 11 12:24:42 2021] test invoked oom-killer: gfp_mask=0x62****(GFP_HIGHUSER_MOVABLE|__GFP_ZERO), nodemask=(null), order=0, oom_score_adj=0
566 [六 9月 11 12:24:42 2021] test cpuset=/ mems_allowed=0
567 [六 9月 11 12:24:42 2021] CPU: 1 PID: 29748 Comm: test Kdump: loaded Not tainted 4.19.91-24.1.al7.x86_64 #1
568 [六 9月 11 12:24:42 2021] Hardware name: Alibaba Cloud Alibaba Cloud ECS, BIOS e62**** 04/01/2014

可能原因

系统出现OOM Killer表示内存不足,内存不足可以分为实例全局内存不足和实例内cgroup的内存不足。这里只贴出实例全局内存不足:原文链接:出现OOM Killer的原因与解决方案

[六 9月 11 15:22:46 2021] insmod invoked oom-killer: gfp_mask=0x60****(GFP_KERNEL), nodemask=(null), order=3, oom_score_adj=0
[六 9月 11 15:22:46 2021] insmod cpuset=/ mems_allowed=0
[六 9月 11 15:22:46 2021] Task in /user.slice killed as a result of limit of host
[六 9月 11 15:22:46 2021] Node 0 Normal free:23500kB min:15892kB low:19864kB high:23836kB active_anon:308kB inactive_anon:194492kB active_file:384kB inactive_file:420kB unevi    ctable:0kB writepending:464kB present:917504kB managed:852784kB mlocked:0kB kernel_stack:2928kB pagetables:9188kB bounce:0kB
[六 9月 11 15:22:46 2021] Node 0 Normal: 1325*4kB (UME) 966*8kB (UME) 675*16kB (UME) 0*32kB (M) 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB =

上面日志记录的出现OOM Killer场景示例中,部分日志记录分析说明:

  • 操作系统在内存分配的order=3阶段出现了OOM Killer。
  • 内存节点Node 0的空闲内存(free)仍高于内存最低水位线(low
    free:23500kB   low:19864kB
  • 内存节点Node 0对应的伙伴系统内存为0(0*32kB (M))。

 原因:操作系统的内存在进行内存分配的过程中,如果伙伴系统的内存不足,则系统会通过OOM Killer释放内存,并将内存提供至伙伴系统。

解决方案

阿里云提供了以下解决方案,您可以结合实际情况,排查并解决问题:

方案序号 方案说明
方案一

建议您自行评估实例内当前占用内存的进程情况,及时清理不需要的进程,以释放内存。如果您的业务所需的内存较大,当前实例规格不满足您对内存的需求,可以升配实例以提升实例的内存容量。更多信息,请参见升降配方式概述。在升配实例后,您还需要根据内存实际的提升情况,手动调整cgroup的内存上限。

调整cgroup内存上限的命令说明如下:
echo value > /sys/fs/cgroup/memory/test/memory.limit_in_bytes
其中,value为您为cgroup设置的内存上限。
方案四 内存碎片化时导致的OOM Killer,建议您定期在业务空闲时间段,进行内存整理。开启内存整理功能的命令为:
echo 1 > /proc/sys/vm/compact_memory

Linux 内核根据应用程序的要求分配内存,通常来说应用程序分配了内存但是并没有实际全部使用,为了提高性能,这部分没用的内存可以留作它用,这部分内存是属于每个进程的,内核直接回收利用的话比较麻烦,所以内核采用一种过度分配内存(over-commit memory)的办法来间接利用这部分 “空闲” 的内存,提高整体内存的使用效率。一般来说这样做没有问题,但当大多数应用程序都消耗完自己的内存的时候麻烦就来了,因为这些应用程序的内存需求加起来超出了物理内存(包括 swap)的容量,内核(OOM killer)必须杀掉一些进程才能腾出空间保障系统正常运行。用银行的例子来讲可能更容易懂一些,部分人取钱的时候银行不怕,银行有足够的存款应付,当全国人民(或者绝大多数)都取钱而且每个人都想把自己钱取完的时候银行的麻烦就来了,银行实际上是没有这么多钱给大家取的。

内核检测到系统内存不足、挑选并杀掉某个进程的过程可以参考内核源代码 linux/mm/oom_kill.c,当系统内存不足的时候,out_of_memory() 被触发,然后调用 select_bad_process() 选择一个 “bad” 进程杀掉,如何判断和选择一个 “bad” 进程呢,总不能随机选吧?挑选的过程由 oom_badness() 决定,挑选的算法和想法都很简单很朴实:最 bad 的那个进程就是那个最占用内存的进程。

理解了这个算法我们就理解了为啥 MySQL 躺着也能中枪了,因为它的体积总是最大(一般来说它在系统上占用内存最多),所以如果 Out of Memeory (OOM) 的话总是不幸第一个被 kill 掉。解决这个问题最简单的办法就是增加内存,或者想办法优化 MySQL 使其占用更少的内存,除了优化 MySQL 外还可以优化系统(优化 Debian 5,优化 CentOS 5.x),让系统尽可能使用少的内存以便应用程序(如 MySQL) 能使用更多的内存,还有一个临时的办法就是调整内核参数,让 MySQL 进程不容易被 OOM killer 发现。

从上面的 oom_kill.c 代码里可以看到 oom_badness() 给每个进程打分,根据 points 的高低来决定杀哪个进程,这个 points 可以根据 adj 调节,root 权限的进程通常被认为很重要,不应该被轻易杀掉,所以打分的时候可以得到 3% 的优惠(adj -= 30; 分数越低越不容易被杀掉)。我们可以在用户空间通过操作每个进程的 oom_adj 内核参数来决定哪些进程不这么容易被 OOM killer 选中杀掉。比如,如果不想 Mysql 进程被轻易杀掉的话可以找到 Mysql 运行的进程号后,调整 oom_score_adj 为 -15(注意 points 越小越不容易被杀):

# ps aux | grep mysqld
mysql    2196  1.6  2.1 623800 44876 ?        Ssl  09:42   0:00 /usr/sbin/mysqld
 
# cat /proc/2196/oom_score_adj
0
# echo -15 > /proc/2196/oom_score_adj

Tomcat进程被杀掉也是基于此原理:

总结:

应用程序申请分配内存,并不会立马全部使用,只要这些应用程序的实际使用内存加起来 没有超出了物理内存(包括 swap)的容量,就相安无事,但是应用程序使用过程中会增加,直到增加到申请的最大内存为止。这个过程中就可能会导致实际使用内存加起来超出了物理内存(包括 swap)的容量就被内核OOM killer,这个过程我们tomcat并没有到达自己申请的最大内存,但是就是因为自己评分低,也就被无故被杀掉。

解决办法

1、增加Linux内存

2、减小jvm最大堆内存 -Xmx=16G

3、增加 swap 交换区大小

    Linux 增加swap 分区大小

4、调节tomcat的oom_score_adj参数,

#查看java进程pid,默认为0
cat /proc/进程pid/oom_score_adj
0

#修改java进程的oom_score_adj,越小越不易被系统内核杀掉
echo -15 > /proc/进程pid/oom_score_adj

参考:
出现OOM Killer的原因与解决方案

Linux 下的 OOM Killer理解和配置_boonya的博客-CSDN博客

你可能感兴趣的:(java,java,tomcat,jvm)