2023-02-28  阅读(0)
原文作者:铁锚 原文地址:https://blog.csdn.net/renfufei/category_9268307.html

为了理解这个错误,我们先回顾一下操作系统相关的基础知识。

我们知道, 操作系统(operating system)构建在进程(process)的基础上. 进程由内核作业(kernel jobs)进行调度和维护, 其中有一个内核作业称为 “Out of memory killer(OOM终结者)”, 与本节所讲的 OutOfMemoryError 有关。

Out of memory killer 在可用内存极低的情况下会杀死某些进程。只要达到触发条件就会激活, 选中某个进程并杀掉。 通常采用启发式算法, 对所有进程计算评分(heuristics scoring), 得分最低的进程将被 kill 掉。因此 Out of memory: Kill process or sacrifice child 和前面所讲的 OutOfMemoryError 都不同, 因为它既不由JVM触发,也不由JVM代理, 而是系统内核内置的一种安全保护措施。

202302282237224501.png

如果可用内存(含swap)不足, 就有可能会影响系统稳定, 这时候 Out of memory killer 就会设法找出流氓进程并杀死他, 也就是引起 Out of memory: kill process or sacrifice child 错误。

原因分析

默认情况下, Linux kernels(内核)允许进程申请的量超过系统可用内存. 这是因为,在大多数情况下, 很多进程申请了很多内存, 但实际使用的量并没有那么多.
有个简单的类比, 宽带租赁的服务商, 可能他的总带宽只有 10Gbps, 但却卖出远远超过100份以上的 100Mbps 带宽, 原因是多数时候, 宽带用户之间是错峰的, 而且不可能每个用户都用满服务商所承诺的带宽。

这样的话,可能会有一个问题, 假若某些程序占用了大量的系统内存, 那么可用内存量就会极小, 导致没有内存页面(pages)可以分配给需要的进程。可能这时候会出现极端情况, 就是 root 用户也不能通过 kill 来杀掉流氓进程. 为了防止发生这种情况, 系统会自动激活 killer, 查找流氓进程并将其杀死。

更多关于 ”Out of memory killer“ 的性能调优细节, 请参考: RedHat 官方文档.

现在我们知道了为什么会发生这种问题, 那为什么是半夜5点钟触发 “killer” 发报警信息给你呢? 通常触发的原因在于操作系统配置. 例如, /proc/sys/vm/overcommit_memory 配置文件的值, 指定了是否允许所有的 malloc() 调用成功. 请注意, 在各操作系统中, 这个配置对应的 proc 文件路径可能不同。

过量使用(overcommitting)配置, 允许流氓进程申请越来越多的内存, 最终惹得 ”Out of memory killer“ 出来搞事情。

示例

在Linux上(如最新稳定版的Ubuntu)编译并执行以下的示例代码:

    package eu.plumbr.demo;
    
    public class OOM {
    
    public static void main(String[] args){
      java.util.List<int[]> l = new java.util.ArrayList();
      for (int i = 10000; i < 100000; i++) {
          try {
            l.add(new int[100_000_000]);
          } catch (Throwable t) {
            t.printStackTrace();
          }
        }
      }
    }

将会在系统日志中(如 /var/log/kern.log 文件)看到一个错误, 类似这样:

    Jun  4 07:41:59 plumbr kernel: 
        [70667120.897649]
        Out of memory: Kill process 29957 (java) score 366 or sacrifice child
    Jun  4 07:41:59 plumbr kernel: 
        [70667120.897701]
        Killed process 29957 (java) total-vm:2532680kB, anon-rss:1416508kB, file-rss:0kB

提示 : 可能需要调整 swap 的大小并设置最大堆内存, 例如堆内存配置为 -Xmx2g, swap 配置如下:

    swapoff -a 
    dd if=/dev/zero of=swapfile bs=1024 count=655360
    mkswap swapfile
    swapon swapfile

解决方案

有多种处理办法。最简单的办法就是将系统迁移到内存更大的实例中。

另外, 还可以通过 OOM killer 调优, 或者做负载均衡(水平扩展,集群), 或者降低应用对内存的需求。

不太推荐的方案是加大交换空间/虚拟内存(swap space)。 试想一下, Java 包含了自动垃圾回收机制, 增加交换内存的代价会很高昂. 现代GC算法在处理物理内存时性能飞快, 但对交换内存来说,其效率就是硬伤了. 交换内存可能导致GC暂停的时间增长几个数量级, 因此在采用这个方案之前, 看看是否真的有这个必要。

原文链接: https://plumbr.eu/outofmemoryerror/kill-process-or-sacrifice-child


Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文