在 Linux 上防止内存不足 (OOM) 冻结的最佳方法是啥?

Posted

技术标签:

【中文标题】在 Linux 上防止内存不足 (OOM) 冻结的最佳方法是啥?【英文标题】:What is the best way to prevent out of memory (OOM) freezes on Linux?在 Linux 上防止内存不足 (OOM) 冻结的最佳方法是什么? 【发布时间】:2011-01-08 17:00:49 【问题描述】:

有没有办法让 OOM 杀手工作并防止 Linux 冻结?我一直在运行 Java 和 C# 应用程序,通常会使用分配的任何内存,并且(如果我理解正确的话)过度使用会导致机器死机。现在,作为临时解决方案,我补充说,

vm.overcommit_memory = 2
vm.overcommit_ratio = 10

到 /etc/sysctl.conf。

感谢任何能够解释为什么现有的 OOM 杀手不能以有保证的方式正常运行的人,只要内核用完“真实”内存就会终止进程。

编辑——许多回答都与 Michael 的“如果您遇到与 OOM 杀手相关的问题,那么您可能需要修复导致您内存不足的任何问题”类似。我认为这不是正确的解决方案。总会有有错误的应用程序,我想调整内核,这样我的整个系统就不会死机。以我目前的技术理解,这似乎不是不可能的。

【问题讨论】:

为了限制内存,你为什么不把overcommit限制为奇偶校验呢? 我的 linux 系统上的 OOM 杀手似乎按设计工作。您有多确定自己正在经历 OOM 杀手级故障?为什么你认为这是原因?您是否也考虑过垃圾收集器问题的可能性? @dmckee -- 所有其他应用程序冻结。 @wallyk - 什么是“将过度使用限制为奇偶校验”? 问题是OOM杀手没有足够快地激活。首先,内核会丢弃所有缓存,这会使您的系统冻结。这是内核设计错误和/或发行版配置错误,应该由内核开发人员修复。不幸的是,这个问题已经存在很多年了,并且没有得到解决,因为人们坚持使用诸如“购买更多 RAM”之类的非解决方案,这显然不能解决根本问题。您可以通过在 OOM 死机 (SysRq+F) 冻结时手动运行它来更快地恢复,但这充其量只是一种解决方法。 最好的办法是让 Linux 修复这个错误——这可能是有史以来最烦人的 Linux 问题。它于 2007 年提交 - System freeze on high memory usage。甚至 Windows 通过显示警告用户内存不足的对话框来防止这种情况发生。 【参考方案1】:

下面是我编写的一个非常基本的 perl 脚本。稍作调整可能会很有用。您只需将我拥有的路径更改为使用 Java 或 C# 的任何进程的路径。您也可以更改我用来重新启动命令的 kill 命令。 当然,为了避免手动输入 perl memusage.pl,您可以将其放入您的 crontab 文件中以自动运行。您还可以使用 perl memusage.pl > log.txt 将其输出保存到日志文件中。对不起,如果它没有真正帮助,但我喝杯咖啡时很无聊。 :-D 干杯

#!/usr/bin/perl -w
# Checks available memory usage and calculates size in MB
# If free memory is below your minimum level specified, then
# the script will attempt to close the troublesome processes down
# that you specify. If it can't, it will issue a -9 KILL signal.
#
# Uses external commands (cat and pidof)
#
# Cheers, insertable

our $memmin = 50;
our @procs = qw(/usr/bin/firefox /usr/local/sbin/apache2);

sub killProcs

    use vars qw(@procs);
    my @pids = ();
    foreach $proc (@procs)
    
        my $filename=substr($proc, rindex($proc,"/")+1,length($proc)-rindex($proc,"/")-1);
        my $pid = `pidof $filename`;
        chop($pid);
        my @pid = split(/ /,$pid);
        push @pids, $pid[0];
    
    foreach $pid (@pids)
    
        #try to kill process normall first
        system("kill -15 " . $pid); 
        print "Killing " . $pid . "\n";
        sleep 1;
        if (-e "/proc/$pid")
        
            print $pid . " is still alive! Issuing a -9 KILL...\n";
            system("kill -9 " + $pid);
            print "Done.\n";
         else 
            print "Looks like " . $pid . " is dead\n";
        
    
    print "Successfully finished destroying memory-hogging processes!\n";
    exit(0);


sub checkMem

    use vars qw($memmin);
    my ($free) = $_[0];
    if ($free > $memmin)
    
        print "Memory usage is OK\n";
        exit(0);
     else 
        killProcs();
    


sub main

    my $meminfo = `cat /proc/meminfo`;
    chop($meminfo);
    my @meminfo = split(/\n/,$meminfo);
    foreach my $line (@meminfo)
    
        if ($line =~ /^MemFree:\s+(.+)\skB$/)
        
            my $free = ($1 / 1024);
            &checkMem($free);
        
    


main();

【讨论】:

不错,但可能不太可靠。也许硬ulimits会起作用?不过,我似乎无法让他们... 抱歉,您想用硬 ulimit 做什么?请记住,您只能将硬限制设置为 root。我相信 /etc/security/limits.conf 中有额外的配置。【参考方案2】:

如果您的进程的 oom_adj 设置为 -17,则不会考虑杀死它,尽管我怀疑这是这里的问题。

cat /proc/<pid>/oom_adj

将告诉您进程的 oom_adj 的值。

【讨论】:

如果cat /proc/<pid>/oom_adj 不起作用,请使用cat /proc/<pid>/oom_score_adj【参考方案3】:

我编写了一个简单的脚本,用于在启动时设置 OOM 分数。所有子流程都会继承这个分数。

#!/usr/bin/env sh

if [ -z "$1" ] || [ -z "$2" ]; then
  echo "Usage: $(basename "$0") oom_score_adj command [args]..."
  echo "  oom_score_adj  A score between -1000 and 1000, bigger gets killed first"
  echo "  command        The command to run"
  echo "  [args]         Optional args for the command to run"
  exit 1
fi

set -eux

echo $1 > /proc/self/oom_score_adj
shift
exec $@

脚本将本地进程的分数设置为提供的第一个参数。这可以是 -1000 到 1000 之间的任何值,其中 1000 最有可能首先被杀死。然后将其余参数作为带有 args 的命令执行,替换当前进程。

【讨论】:

【参考方案4】:

我不得不说防止 OOM 冻结的最佳方法是不要耗尽虚拟内存。如果您经常用完虚拟内存,或者越来越接近,那么您的问题就更大了。

大多数任务不能很好地处理失败的内存分配,因此往往会崩溃或丢失数据。用完虚拟内存(有或没有过度使用)将导致某些分配失败。这通常很糟糕。

此外,在您的操作系统耗尽虚拟内存之前,它会开始做一些坏事,例如从常用共享库中丢弃页面,这可能会降低性能,因为它们必须经常被拉回,这非常糟糕吞吐量。

我的建议:

获取更多内存 运行更少的进程 让您运行的进程使用更少的内存(这可能包括修复其中的内存泄漏)

还有可能

设置更多交换空间

如果这对您的用例有帮助。

大多数多进程服务器运行可配置(最大)数量的进程,因此您通常可以将其调低。多线程服务器通常允许您在内部配置用于缓冲区等的内存量。

【讨论】:

我犯了一个错误:我试图查看一个 55 GB 的 XML 文件。几十秒后,机器冻结了。我怀疑部分问题是我的交换文件不是 55 GB。然而,让用户空间应用程序瘫痪整个系统是糟糕的设计。恕我直言,如果单个进程正在使用过多的虚拟 RAM,那么内核应该杀死 那个 进程,从而释放虚拟 RAM,并允许系统的其余部分继续运行。用户应用程序应该不可能使系统进入唯一的办法是重新启动的状态。【参考方案5】:

首先,您如何确定冻结与 OOM 杀手相关?我在该领域有一个系统网络,并且经常出现冻结,这似乎与 OOM 无关(我们的应用程序在内存使用方面非常稳定)。会不会是别的东西?是否涉及任何有趣的硬件?有不稳定的驱动吗?高性能视频?

即使 OOM 杀手参与其中并且起作用了,您仍然会遇到问题,因为您认为正在运行的东西现在已经死了,谁知道它会留下什么样的混乱。

真的,如果您遇到与 OOM 杀手相关的问题,那么您可能需要修复导致内存不足的任何问题。

【讨论】:

一次或两次,我已经能够在一切冻结之前拉起系统监视器。【参考方案6】:

我发现解决稳定性问题主要依赖于准确识别根本原因。不幸的是,这需要能够在问题发生时看到正在发生的事情,这是尝试启动各种监控程序的非常糟糕的时间。

我有时发现有用的一件事是在启动时启动一个小监控脚本,它会记录各种有趣的数字并快照正在运行的进程。然后,如果发生碰撞,我可以查看碰撞前的情况。我有时发现直觉对于根本原因是完全错误的。不幸的是,该脚本早已过时,或者我会提供一个链接。

【讨论】:

以上是关于在 Linux 上防止内存不足 (OOM) 冻结的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

如何防止 Spring Boot / Tomcat (Java8) 进程被 OOM 杀死?

Linux OOM Killer

linux--OOM killer

linux--OOM killer

OOM killer机制

图解 K8S OOM 和 CPU 节流