当 Linux OOM Killer 中断进程时,内核日志通常会提供有关罪魁祸首的内存消耗的足够信息(即使它最终没有被杀死)。例如,当snmpd
进程成为 OOM 触发器时,稍后可以通过以下方式在日志中找到其内存状态PID=1190
:
Jul 18 02:21:26 inm-agg kernel: snmpd invoked oom-killer: gfp_mask=0x100cca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0
Jul 18 02:21:26 inm-agg kernel: CPU: 3 PID: 1190 Comm: snmpd Kdump: loaded Not tainted 5.4.17-2102.201.3.el8uek.x86_64 #2
...
Jul 18 02:21:26 inm-agg kernel: Tasks state (memory values in pages):
Jul 18 02:21:26 inm-agg kernel: [ pid ] uid tgid total_vm rss pgtables_bytes swapents oom_score_adj name
...
Jul 18 02:21:26 inm-agg kernel: [ 1190] 0 1190 78491 1761 217088 0 0 snmpd
然而,当同样的情况发生在 Java 应用程序的线程上时(OpenJDK 64-Bit Server VM (build 25.372-b07, mixed mode)
在我的例子中),日志包含一个与任何进程都不对应的PID 。例如,在以下日志中,Apache Cassandra 的输入处理线程ReadStage-150
已成为 OOM 触发器:
Jul 16 22:01:45 inm-agg kernel: ReadStage-150 invoked oom-killer: gfp_mask=0x100dca(GFP_HIGHUSER_MOVABLE|__GFP_ZERO), order=0, oom_score_adj=0
Jul 16 22:01:45 inm-agg kernel: CPU: 11 PID: 1653163 Comm: ReadStage-150 Kdump: loaded Not tainted 5.4.17-2102.201.3.el8uek.x86_64 #2
但PID=1653163
消息中指定的内容没有在其他地方提到:
$ journalctl -k -b -e | grep "1653163" | wc -l
1
它与 Java 进程 PID 本身没有任何共同点(1652432
):
Jul 16 22:01:45 inm-agg kernel: Tasks state (memory values in pages):
Jul 16 22:01:45 inm-agg kernel: [ pid ] uid tgid total_vm rss pgtables_bytes swapents oom_score_adj name
…
Jul 16 22:01:45 inm-agg kernel: [1652432] 0 1652432 7256008 5839621 49709056 0 0 java
所以我想知道:
- oom-killer消息的PID来自哪里?
- 在这种情况下,为什么线程与其托管 JVM 进程分开处理?
- 如果将 oom-killer 配置为杀死 OOM 发起者,是否有可能(至少在理论上)仅中断罪魁祸首线程而不是整个 JVM?
Linux 内核所称的 PID 又名任务,严格来说并不等于 ps 或 top 所称的 PID。内核 PID 具有标识“重量级”进程的任务组 ID (TGID)。重是指在某些多线程程序中,多个 PID 共享 TGID 和内存。因此,在某些性能监控工具中可以看到java进程使用超过100%的CPU。
开头的“invoked oom-killer”标题行显示了 CPU 上的不幸任务,以及到该点的堆栈。这可能不是 OOM 的“归咎”任务,并且如果未设置 sysctl oom_kill_allocing_task,它也可能不会被终止。但它可能只是做了内存分配。
“任务状态”列表(如果通过 sysctl 启用):
换句话说,这是尽力列出系统上可能被终止的进程。注意“tgid”是一列,用于帮助跟踪多线程线程组。启用 cgroup 后,例如使用 systemd 包含单元时,这个列表比整个系统要短得多。
内核对任务“坏度”进行非常基本的猜测,主要基于该任务与总系统内存页的比率。任何“杀死进程”消息都会显示受害者任务的详细信息,通过 SIGKILL 强制终止。该信号意味着整个线程组被终止。
这些任务都没有被证明是“罪魁祸首”。这仅仅是内核可以轻松向您显示的内容:CPU 上的内容、为方便起见还有一些带有 TGID 的任务,以及杀死具有相对大量页面的内容可能会拯救系统。
意识到内存不足是一种可怕的情况。系统正在考虑程序崩溃并可能导致数据丢失。没有太多发挥聪明才智的空间。
如果有的话,您的努力和聪明应该用于改进您的容量规划。了解这些服务如何在服务管理器和容器中包含内存。按 cgroup 和系统范围观察内存消耗。提出一个内存大小调整算法,无论多少GB用于服务,一点用于内核和管理,以及一些百分之几的安全裕度。进行调整,直到不再被 OOM 杀死。