我通过 Vagrant 在具有 350 MB RAM 和 2 GB 交换空间的虚拟机上运行 FreeBSD 12。
如果我运行:
perl -e '$a="x"x200_000_000;sleep 1000' & sleep 10
perl -e '$a="x"x200_000_000;sleep 1000' & sleep 10
perl -e '$a="x"x200_000_000;sleep 1000' & sleep 10
perl -e '$a="x"x200_000_000;sleep 1000' & sleep 10
perl -e '$a="x"x200_000_000;sleep 1000' & sleep 10
它达到了我的预期:启动 5 个进程,每个进程消耗 400 MB(每个进程 2*200MB)。
但是,如果我删除sleep 10
:
perl -e '$a="x"x200_000_000;sleep 1000' &
perl -e '$a="x"x200_000_000;sleep 1000' &
perl -e '$a="x"x200_000_000;sleep 1000' &
perl -e '$a="x"x200_000_000;sleep 1000' &
perl -e '$a="x"x200_000_000;sleep 1000' &
FreeBSD 杀死了 2 个工作岗位。
为什么?
我怎样才能避免它?
如果我运行超过 5 个:
perl -e '$a="x"x200_000_000;sleep 1000' & sleep 10
然后我预计最新的会因为内存不足而被杀死。但 FreeBSD 似乎随机选择了一个 perl 作业,然后将其杀死。
这是来自的屏幕截图top
:
last pid: 3529; load averages: 1.45, 0.62, 0.34 up 0+00:17:14 21:49:30
29 processes: 5 running, 24 sleeping
CPU: 0.6% user, 0.0% nice, 34.5% system, 6.6% interrupt, 58.3% idle
Mem: 150M Active, 1080K Inact, 41M Laundry, 108M Wired, 39M Buf, 2256K Free
Swap: 2423M Total, 873M Used, 1550M Free, 36% Inuse, 33M In, 29M Out
PID USERNAME THR PRI NICE SIZE RES STATE C TIME WCPU COMMAND
3521 vagrant 1 29 0 400M 53M RUN 1 0:03 25.98% perl
3525 vagrant 1 24 0 400M 52M RUN 1 0:03 4.11% perl
3527 vagrant 1 25 0 400M 53M RUN 0 0:02 4.09% perl
3523 vagrant 1 24 0 400M 52M RUN 1 0:03 3.53% perl
807 root 7 52 0 20M 1672K sigwai 1 0:00 0.07% VBoxService
答案1
太长了;
为什么?
当资源耗尽时,无法保证谁能生存。大量的内存使用会给你带来一个目标。
我怎样才能避免它?
分配内存时要小心。不要分配超出您需要的内容。如果您急于分配大量内存,请监视“空闲”内存以确保不会触发 OOM。
看FreeBSD 内存 Wiki和FreeBSD如何分配内存?
小心这里有龙
这只是一个猜测。你没有触及它,所以我会提到你可能会发现显而易见的内容:
您的系统资源有限。只有 350 MB RAM,但交换空间足以满足您正在做的事情。只要有一个非常小的睡眠,页面输出守护进程就有时间运行并交换一些或所有脏页面。没有任何延迟,您就已经开始了一场比赛。
活动队列和非活动队列包含干净页和脏页。当回收内存以缓解短缺时,页面守护程序将从非活动队列的头部释放干净的页面。脏页必须首先通过将其分页到交换或文件系统来清除。这是一项繁重的工作,因此页面守护进程将它们移至洗衣队列以进行延迟处理。
——摘自优秀文章探索 FreeBSD 上的交换
值得注意的是,在 NUMA 系统上,您可能有多个页面守护进程和洗衣线程。
你期望他们应该按顺序被杀死。但系统并没有提供这样的保证。这是一个边缘情况,系统完全黑手党“男人就必须做男人该做的事”。在幕后会有某种“秩序”:
为了找到要杀死的进程,内核会估计每个可运行进程的内存使用情况,并选择使用量最大的进程。
但你们的流程是相同的,所以没有帮助。然后,如果您的进程完全按顺序运行,或者某些其他系统进程发出小声并按洗衣清单中进程的页面顺序排列,那么我们将讨论细节。因此,当我们出现 OOM 时,系统会尝试生存,但它不会为您提供确定性的保证。
您可能会争辩说您希望遵守进程优先级(良好级别),或者您可以设置特定的内存优先级。不存在这样的东西我的知识。您可以保护您的进程免受 OOM 的影响,但这应该保留用于真的 真的 真的重要且经过充分测试的系统服务。
仔细观察
当事情变坏时,它们会记录到控制台日志中。如果调试比这更复杂的情况,它会很有帮助。
您主要会寻找这样的东西:
pid . . ., was killed: failed to reclaim memory
pid . . ., was killed: a thread waited too long to allocate a page
pid . . ., was killed: out of swap space
kernel: swap_pager
您可能想查看其中的几个日志文件,/var/log
但您会发现最有趣的是:
dmesg | grep -e killed -e swap_pager
调音
如果您想调整 OOM 行为,以下 sysctls 将会引起您的兴趣:
vm.pageout_oom_seq: back-to-back calls to oom detector to start OOM
vm.panic_on_oom: panic on out of memory instead of killing the largest process
vm.pfault_oom_wait: Number of seconds to wait for free pages before retrying the page fault handler
vm.pfault_oom_attempts: Number of page allocation attempts in page fault handler before it triggers OOM handling
vm.v_pageout_free_min: Min pages reserved for kernel
vm.pageout_oom_seq: back-to-back calls to oom detector to start OOM
vm.pageout_lock_miss: vget() lock misses during pageout
vm.disable_swapspace_pageouts: Disallow swapout of dirty pages
vm.pageout_update_period: Maximum active LRU update period
例如vm.pageout_oom_seq
默认为 12。如果降低此值,则 OOM 对缺少 pagedaemon 进度会更加敏感。
你不应该做什么
当你深入研究这个问题时,你会发现一些有趣的标志。您的案例自然是一个例子,因此我们不知道您到底想要实现什么目标。
如果你看附注(1)你会找到旗帜P_SWAPPINGOUT
。根据您正在做的事情,您可能会密切关注,看看您自己的进程是否实际上正忙于交换。在现实世界中,您最好在分配之前检查可用内存(我认为)。
看着这些标志,当您看到 时,您可能会受到无理的诱惑P_PROTECTED
。在其他地方,这也称为 madvise(MADV_PROTECT)。这些是 OOM 时受到保护的进程。
做不是用那个。
使用以下命令可以轻松为进程设置此值保护(1)
做不是用那个。
对于 rc 服务,可以通过将${name}_oomprotect
rc.conf
变量设置为 来设置“YES”
。
做不是用那个。
如果有合理的系统设置和“足够的”交换空间让系统慢慢停止。
当您编写了一个以合理方式分配内存的进程时,您可能会考虑它。只有当这个进程对我来说真的很重要并且我知道我还有其他行为不当的进程,这些进程要么过度使用内存分配,要么存在泄漏时,我才会这样做。
有一篇文章不错防止 FreeBSD 杀死 PostgreSQL(又名 OOM Killer 预防)但我自己不会这么做。当你试图智胜系统时,你就处于危险境地。 OOM 是一种生存模式,你不应该写任何依赖于世界有序崩溃的东西。