性能文章>OOM系列之八:java.lang.OutOfMemoryError: Kill process or sacrifice child>

OOM系列之八:java.lang.OutOfMemoryError: Kill process or sacrifice child转载

2年前
5841017

第一篇:java.lang.OutOfMemoryError:Java heap space

第二篇:Java.lang.OutOfMemoryError: GC overhead limit exceeded

第三篇:java.lang.OutOfMemoryError: 永久空间

第四篇:java.lang.OutOfMemoryError: 元空间

第五篇:java.lang.OutOfMemoryError: 无法创建新的本地线程

第六篇:java.lang.OutOfMemoryError: 交换空间不足

第七篇:java.lang.OutOfMemoryError: 请求的数组大小超出限制

第八篇:java.lang.OutOfMemoryError: Kill process or sacrifice child

正文:

为了理解这个错误,我们需要回顾一下操作系统的基础知识。如您所知,操作系统是建立在进程的概念之上的。这些进程由几个内核作业引导,在这种特殊情况下,我们对其中一个名为“OOM Killer”的内容很感兴趣。

此内核作业可以在极低的内存条件下消灭您的进程。当检测到这种情况时,OOM Killer会被激活并选择一个进程来杀死。使用一组启发式对所有进程进行评分并选择得分最差的进程来选择目标。因此,OOM:Kill process or sacrifice child与我们的OOM 手册中涵盖的其他错误不同,因为它不是由 JVM 触发或代理的,而是内置于操作系统内核中的安全网。

内存不足 linux 内核

输出存储器:Kill process or sacrifice child时可用虚拟内存(包括交换)被产生误差被消耗到整个操作系统稳定性被投入风险的程度。在这种情况下,内存不足杀手会选择流氓进程并杀死它。

1,是什么原因造成的?

默认情况下,Linux 内核允许进程请求比系统中当前可用的内存更多的内存。考虑到大多数进程实际上从未使用过它们分配的所有内存,这在世界上都是有意义的。与这种方法最简单的比较是宽带运营商。他们向所有消费者出售 100Mbit 下载承诺,远远超过他们网络中的实际带宽。赌注再次取决于用户不会同时全部使用他们分配的下载限制这一事实。因此,一个 10Gbit 链路可以成功地为超过 100 个用户提供我们简单的数学所允许的服务。

如果您的某些程序正在耗尽系统内存,则这种方法的副作用是可见的。这可能导致内存极低的情况,即无法分配任何页面来处理。您可能遇到过这样的情况,即使是 root 帐户也无法杀死有问题的任务。为了防止出现这种情况,杀手会激活并识别流氓进程被杀死。

您可以在这篇来自 RedHat 文档的文章中阅读有关微调“OOM Killer”行为的更多信息。

现在我们有了上下文,你怎么知道是什么触发了“杀手”并在凌晨 5 点叫醒你?一种常见的激活触发器隐藏在操作系统配置中。当您检查/proc/sys/vm/overcommit_memory 中的配置时,您会得到第一个提示——此处指定的值表示是否允许所有 malloc() 调用成功。请注意,proc 文件系统中参数的路径因受更改影响的系统而异。

过度使用配置允许为这个流氓进程分配越来越多的内存,这最终会触发“OOM Killer”来做它应该做的事情。

2,举个例子

当您在 Linux 上编译并启动以下 Java 代码片段时(我使用了最新的稳定版 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

请注意,您可能需要调整交换文件和堆大小,在我们的测试用例中,我们使用了-Xmx2g指定的 2g 堆并具有以下交换配置:

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

 

3,解决办法是什么?

有几种方法可以处理这种情况。解决该问题的第一个也是最直接的方法是将系统迁移到具有更多内存的实例。

其他可能性包括微调 OOM 杀手、跨几个小实例水平扩展负载或减少应用程序的内存需求。

我们不热衷推荐的一种解决方案是增加交换空间。当您回忆起 Java 是一种垃圾收集语言时,此解决方案似乎已经不那么有利可图了。现代 GC 算法在物理内存中运行时是高效的,但在处理交换分配时效率受到打击。交换可以将 GC 暂停的长度增加几个数量级,因此在跳转到此解决方案之前应该三思而后行。

 

点赞收藏
金色梦想

终身学习。

请先登录,感受更多精彩内容
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

从 Linux 内核角度探秘 JDK MappedByteBuffer

从 Linux 内核角度探秘 JDK MappedByteBuffer

MappedByteBuffer VS FileChannel:从内核层面对比两者的性能差异

MappedByteBuffer VS FileChannel:从内核层面对比两者的性能差异

17
0