OOM Killer(Out-Of-Memory Killer)的触发通常是由于Linux系统内存不足时,内核采取的一种极端保护措施。具体来说,OOM Killer触发的原因主要有以下几个方面:
一、触发OOM Killer原因
- 系统内存耗尽
当系统物理内存(RAM)和交换空间(Swap)都被大量占用,无法满足新的内存分配请求时,OOM Killer会被触发。这通常发生在高负载的服务器上,当多个内存密集型应用同时运行时,系统内存很快就会被耗尽。
- 内存分配失败
当某个进程尝试分配内存,但系统无法满足其请求时,内核会尝试通过回收内存(如页面回收、文件缓存回收等)来释放空间。如果回收的内存仍然不足,OOM Killer将被激活,以选择并终止某些进程来释放内存。
- 内存泄漏
应用程序中的内存泄漏问题也可能导致OOM Killer的触发。内存泄漏是指程序在运行过程中,无法释放已经不再使用的内存空间,导致内存不断被占用,最终耗尽系统资源。
- 特定进程的内存需求
某些进程可能因为内存需求过大,或者请求大块连续内存而触发OOM Killer。在32位系统中,由于地址空间的限制,大内存请求更容易导致问题。
- 系统配置和策略
Linux内核提供了多种配置选项和策略,用于控制OOM Killer的行为。例如,可以通过调整/proc/sys/vm/oom_kill_allocating_task
的值来决定是否杀死尝试分配内存的进程。此外,还可以通过设置进程的OOM得分(oom_score_adj)来调整其被OOM Killer选中的优先级。
- 内核和硬件限制
在某些情况下,内核或硬件的限制也可能导致OOM Killer的触发。例如,在NUMA(Non-Uniform Memory Access)架构的系统中,节点间的内存分配限制可能导致OOM Killer的激活。
当OOM Killer被触发时,内核会计算每个进程的OOM得分,并选择得分最高的进程进行终止。OOM得分通常基于进程的内存使用量、优先级、类型等因素来计算。被终止的进程及其内存释放信息会被记录在系统日志中,以便管理员进行后续分析和处理。
二、排查思路
- 实时监控 :通过系统监控工具(如
top
、htop
、free
、vmstat
等)查看当前系统内存使用情况,包括物理内存和交换空间。 - 检查系统日志 :查看
/var/log/messages
或dmesg
输出,寻找OOM Killer的相关信息,内核会在触发OOM Killer时记录相关信息,包括被杀掉的进程及其理由。 - 分析OOM Killer报告:当OOM Killer启动时,它会生成一个详细的报告,指出为何选择特定进程进行终止。可以通过这些信息来识别最可能的内存消耗大户。
- 定位消耗内存的进程 :利用
ps
、pmap
、smem
等工具定位消耗内存最多的进程,并进一步分析其行为和资源需求。 - 检查程序日志和代码:如果确定是某个应用程序导致的OOM,应查看该程序的日志文件,了解其运行时的内存使用情况。同时,对代码进行审计,查找是否存在内存泄漏或其他不当内存管理。
- 启用内核OOME日志 :在系统启动参数中加入
vm.panic_on_oom=0
和vm.overcommit_memory=2
(或适当调整内存过载策略),以便在发生OOM时让内核生成更详细的日志。 - 长期监控与趋势分析:使用像Prometheus + Grafana这样的监控系统长期收集内存使用数据并进行趋势分析,有助于提前发现潜在的内存使用增长问题。
- 优化资源配置:根据实际情况调整系统或容器的内存限制,优化程序以降低内存消耗,或者考虑增加物理内存和交换空间。
- 针对特定问题解决方案:例如,在图像处理或大数据场景中,采用合适的内存管理和缓存策略,避免一次性加载所有数据到内存中。对于Java应用,可通过调整JVM参数等方式优化内存管理。
三、优化建议
为了避免OOM Killer的触发,可以采取以下措施:
- 优化内存使用:检查并优化应用程序的内存使用,避免内存泄漏和不必要的内存占用。
- 增加内存资源:在系统允许的情况下,增加物理内存或交换空间的大小。
- 调整OOM Killer策略:通过修改系统配置,调整OOM Killer的行为和优先级策略。
- 监控和预警:使用系统监控工具实时监控系统内存使用情况,及时发现并处理内存不足的问题。
四、实例分析
1、ls触发OMM
javascript
Aug 17 03:12:59 Hygonrbej01 kernel: [159440.49643] ls invoked oom-killer: gfp_mask=0x6040c0(GFP_KERNEL|__GFP_COMP), nodemask=(null), order=3, oom_score_adj=0
Aug 17 03:12:59 Hygonrbej01 kernel: [159440.49646] ls cpuset=/ mems_allowed=0
Aug 17 03:12:59 Hygonrbej01 kernel: [159440.49652] CPU: 0 PID: 389525 Comm: ls Kdump: loaded Tainted: GOE 4.19.90-23.26.v2101.ky10.x86_64 #1
这段日志显示了Linux系统中的一个内存不足(OOM, Out of Memory)事件,具体是由ls
命令触发的。ls
命令是一个常用的列出目录内容的命令,但在某些情况下,如果它试图读取或处理大量文件,并且系统内存已经接近耗尽,就可能会触发OOM Killer。不过,在这个特定的例子中,ls
命令本身作为OOM Killer的受害者似乎有些不寻常,因为ls
通常不会消耗大量内存。这可能是由于其他因素导致的,比如系统内存已经极度紧张,或者ls
命令在处理某些特殊文件或目录时遇到了问题。
以下是日志中关键信息的解释:
-
OOM Killer被调用 :日志中的
ls invoked oom-killer
表明ls
进程是触发OOM Killer的"罪魁祸首"。但请注意,这并不一定意味着ls
是内存泄漏或过度使用内存的源头;它可能只是在系统内存已经严重不足时尝试分配内存的众多进程之一。 -
内存分配请求 :
gfp_mask=0x6040c0(GFP_KERNEL|__GFP_COMP)
表示这是一个内核内存分配请求,使用了GFP_KERNEL
和__GFP_COMP
标志。GFP_KERNEL
是内核分配内存时最常用的标志,表示可以等待内存释放;__GFP_COMP
可能是一个与内存压缩相关的标志,但在较新的Linux版本中可能不再使用或已更改含义。 -
节点和CPU信息 :
nodemask=(null)
表明没有特定的NUMA节点偏好;CPU: 0
表明OOM Killer的日志是在CPU 0上记录的。 -
进程信息 :
PID: 389525 Comm: ls
显示了被OOM Killer选中的进程ID(PID)和命令名(Comm)。 -
系统信息:包括硬件名称、BIOS版本等信息,这些信息对于分析系统环境和可能的问题源很有帮助。
-
调用栈 :
Call Trace
部分显示了导致OOM Killer被触发的函数调用栈。这有助于开发者或系统管理员了解在内存分配失败时,系统是如何处理并最终决定杀死某个进程的。 -
内存信息(Mem-Info):这部分提供了关于系统内存使用情况的详细统计,包括活跃和非活跃的内存、slab内存、映射的内存、共享内存等。这些信息对于诊断内存问题非常有用。
2、Java进程被kill9
javascript
Aug 17 03:12:59 Hygonrbej01 kernel: [159440.49894] Out of memory: Kill process 3067998 (java) score 244 or sacrifice child
Aug 17 03:12:59 Hygonrbej01 kernel: [159440.50254] Killed process 367998 (java) total-vm:12717748kB, anon-rss:3581268kB, file-rss:3936kB, shmem-rss:20kB
Aug 17 03:12:59 Hygonrbej01 kernel: [159440.19186] oom_reaper: reaped process 3067998 (java), now anon-rss:0kB, file-rss:0kB, shmem-rss:20kB
Aug 17 03:12:59 Hygonrbej01 systemd[1]: bes.service: Main process exited, code=killed, status=9/KILL
Aug 17 03:12:59 Hygonrbej01 systemd[1]: bes.service: Failed with result 'signal'.
这段日志记录了几个关键事件,主要涉及系统内存管理、服务失败。下面是对这些事件的详细解释:
- 内存不足(OOM Killer 触发) :
Out of memory: Kill process 3067998 (java) score 244 or sacrifice child
:这表示系统遇到了内存不足(Out of Memory, OOM)的情况,OOM Killer 被激活以杀死一些进程以释放内存。它选择了进程ID为3067998的Java进程作为牺牲品,因为这个进程的OOM分数(score)最高,为244。OOM分数是基于进程占用的内存量、优先级等因素计算得出的。
- Java进程被杀死 :
Killed process 3067998 (java) total-vm:12717748kB, anon-rss:3581268kB, file-rss:3936kB, shmem-rss:20kB
:这进一步确认了Java进程(PID 3067998)已被杀死。该进程占用的总虚拟内存(total-vm)为12,717,748kB,匿名可回收内存(anon-rss)为3,581,268kB,文件映射内存(file-rss)为3,936kB,共享内存(shmem-rss)为20kB。
- OOM Reaper 清理 :
oom_reaper: reaped process 3067998 (java), now anon-rss:0kB, file-rss:0kB, shmem-rss:20kB
:OOM Reaper 是一个内核线程,负责清理OOM Killer杀死的进程留下的资源。这里显示Java进程的匿名可回收内存和文件映射内存已被清零,但共享内存(可能是因为它不是由该进程独占的)保持不变。
- 服务失败 :
systemd[1]: bes.service: Main process exited, code=killed, status=9/KILL
和systemd[1]: bes.service: Failed with result 'signal'
:这表明一个名为bes.service
的服务的主进程被信号(这里是KILL信号)杀死,导致服务失败。这很可能与前面提到的Java进程被杀有关,如果该Java进程是该服务的一部分。