Ubuntu的RAM快要用完了,我的电脑开始冻结了。有什么命令可以解决这个问题?

当我在后台编译软件时,经常会发生这种情况:突然一切开始变慢,最终甚至会卡住[如果我什么都不做],因为我已经用完了RAM和交换空间。

这个问题假设我有足够的时间和资源来打开Gnome终端,搜索我的历史记录,并执行一个sudo命令。

有什么命令可以让我避免进行硬重启,或者根本不需要重启吗?


评论不适合进行详细讨论;此对话已被移至聊天室 - Thomas Ward
1如果你的交换空间用完了,我认为你的交换空间太少了。我在这台电脑上有20G的交换空间。关键是它能给你足够的时间来处理占用内存的程序,让系统保持可用状态。这不是只取你会使用的部分,而是希望永远不会使用的部分。 - JoL
1你确定RAM和swap都被占满了吗?如果是的话,内存管理程序会终止你的编译器并释放内存(这也可能导致构建过程出现问题)。否则,我认为只是内存占用过高,可能是因为你的交换空间位于系统磁盘上,导致系统变慢。 - sudo
3如果你的内存不足以支持并行构建,请尝试减少并行构建的数量。如果你的构建开始交换内存,速度会变得非常慢。例如,使用make命令时,可以尝试使用-j4参数,同时进行4个并行构建。 - Shahbaz
1Alexa,给我订购8GB的内存条。 - user12753
12个回答

根据我的经验,Firefox和Chrome使用的内存比我前7台电脑加起来还多。可能不止这些,但我离开了我的重点。你应该做的第一件事就是关闭你的浏览器。一个命令?
killall -9 firefox google-chrome google-chrome-stable chromium-browser

我把最流行的浏览器都绑定在一个命令中,但如果你使用其他浏览器(或者确定你没有使用其中之一),只需修改命令即可。关键是killall -9 ...。人们对SIGKILL(信号编号9)有些犹豫,但浏览器非常强大。此外,通过SIGTERM慢慢终止会导致浏览器进行大量的清理工作,这需要额外的内存,而在这种情况下你无法承受这样的开销。
如果你无法在已运行的终端或Alt+F2对话框中输入命令,请考虑切换到TTY。按下Control + Alt + F2将进入TTY2,这应该允许你登录(可能会比较慢),甚至可以使用像htop这样的工具来调试问题。我从来没有遇到过内存耗尽到无法打开htop的情况。
长期解决方案包括购买更多的RAM,通过远程计算机租用RAM,或者不再进行当前的操作。我将把复杂的经济论证留给您,但总体而言,RAM的购买是廉价的,但如果您只需要一定数量的RAM,按分钟或小时计费的VPS服务器是一个不错的选择。

评论不适合进行长时间讨论;此对话已被移至聊天室 - Thomas Ward
我有一些与我的自定义lazygit命令相关联的命令,我偶尔使用它们,也许这里可以应用类似的东西?整个killall ...脚本可以简化为一个简单的emptyram或类似的东西。 - Francisco Presencia
如果你知道正在运行的浏览器是什么,就不需要运行完整的命令,我认为大多数能够识别内存不足的人都会知道。从这个角度来看,我会觉得更难记住我写了一个“emptyram”脚本,而不是直接输入“killall -9 firefox”。 - Oli
2购买内存条...为什么不直接下载更多的内存呢? - Stephan Bijzitter
1你可能在开玩笑,但是如果你需要在短时间内做一些需要比你拥有的RAM和CPU更多的事情,按分钟租用VPS对于一次性任务来说是相当经济实惠的选择。 - Oli

在启用了魔术系统请求键的系统上,按下 Alt + 系统请求 + f(如果你的键盘上没有标记,系统请求 通常位于 Print Screen 键上)将手动调用内核的内存杀手(oomkiller),它会尝试选择最糟糕的内存使用进程并将其终止。如果你可能没有描述的时间,并且系统即将开始(或者可能已经开始)频繁交换内存,你可以这样做-在这种情况下,你可能不关心具体终止哪个进程,只想要一个可用的系统。有时候这可能会终止 X,但现在大多数情况下,它在选择一个糟糕的进程方面比以前好得多。


2这是一个非常糟糕的主意,如果你的内存不足,因为你正在编译非常复杂的东西。有很大的可能性会导致编译器崩溃,并且丢失到目前为止的所有进展,对于一个非常庞大的项目来说,这可能是一个很大的问题。 - T. Sar
5@T.Sar 如果你直接开始猛攻,要么你已经输了,要么有机会击败吞噬记忆的敌人。如果你只是不采取行动,你不会得到任何好处。 - Ruslan
4@Muzer 只有在你将 kernel.sysrq 设置为 1 或包含正确位数的数字时,这才起作用。请在 /etc/sysctl.d/10-magic-sysrq.conf 文件中进行设置。 - Ruslan
1@Ruslan 我并不是说要停止行动,只是这个特定的命令可能会导致一些不可取的进度损失,也许另一个选项可能是更好的选择。例如,在Windows 7中,插入配置了TurboBoost的闪存驱动器可以很好地解决OOM问题,通过为系统提供更多内存来工作。 - T. Sar
9@T.Sar 如果你使用的是一个合理的构建系统,你不会失去你的进度。你将保留所有的目标文件,除了你实际正在编译的那个文件,然后你将能够回到几乎你离开的地方。 - Muzer
2@T.Sar 我不知道,如果你正在进行并行构建,并且文件之间有相当复杂的依赖关系,我可以理解它会消耗大量内存。再加上通常占用大量内存的电子邮件客户端和同时打开多个标签的网络浏览器,我可以看到它会对性能较弱的系统造成很大压力。 - Muzer
@Muzer 问题是,对于编译器来说,使用中的内存是正在进行的工作。如果编译器需要一开始就加载那么多东西,到了不清理只是不断堆积东西的地步,那肯定不是在构建一个合理的东西。请记住,Linux本身——一个非常庞大且复杂的系统——现在几乎可以由任何开发机器编译。我非常怀疑OP是否在一台低端机器上编译比Linux本身更复杂的东西。 - T. Sar
3@T.Sar 就因为你正在编译的东西不合理,并不意味着构建系统就不合理。自古以来,构建系统一直会存储对象文件以便在后续编译中重复使用。另一方面,我可以轻松列举出许多比 Linux(通常设计得相当好)更不合理的软件项目。例如,使用8个并行构建线程编译类似 Firefox 或 OpenOffice 这样的东西,我很容易看到它可能需要几十GB的内存。还有许多庞大的企业系统依赖于数百个库。 - Muzer
7@T.Sar Linux在编译器的角度来看并不是非常复杂。实际上,几乎没有什么C程序是复杂的。那C++呢?你有没有尝试过使用Eigen或Boost构建程序?你会惊讶地发现,编译器在处理这些程序时有时会占用很多内存,而它们本身并不一定复杂。 - Ruslan
@T.Sar 你是指Linux(在某些构建中使用的内核)还是指GNU coreutils(如bash[man等),或者是指GUI(X服务器,可能是openbox,还是其他类似LXDE的东西),或者是指应用软件(从apt或其他包管理器获取的软件)?其中一些比其他一些更复杂。 - wizzwizz4
2@T.Sar "在Windows 7中,插入配置了TurboBoost的闪存驱动器可能会很好地解决OOM问题" ... 我想你是指ReadyBoost,而不是TurboBoost(TurboBoost是一种CPU频率自适应技术)。ReadyBoost在OOM情况下无法提供帮助 - 它只提供额外的磁盘缓存,而不是额外的虚拟内存。 - Jules
2@T.Sar Linux本身只有2到10MB的编译代码,按照今天的标准来看,它几乎不算是一款复杂的软件。 - Dmitry Grigoryev
2@wizzwizz4:嗯,这就是*nix的关键所在,几乎所有构成“系统”的东西都是相对独立且规模较小的组件。此外,软件的复杂性和内存使用并不与编译的复杂性密切相关。我曾经参与过一些并行应用程序的开发,它们可以使用数百GB的内存,在2GB的笔记本电脑上编译只需要几分钟,并且不会超载内存,而这些应用程序需要运行数天来完成一些相当复杂的计算任务。 - jamesqf
关于丢失进度的问题:@Muzer,我不知道你所说的“合理的构建系统”是什么意思,而且我也没有在Linux上尝试过这个方法。但是,在Visual Studio中取消构建经常会导致我得到一些无法使用的目标文件,我不得不手动删除它们(因为它们只完成了一半)。并不是每个目标文件都是全有或全无的,除非你的编译器是这样处理的。 - user541686
@Mehrdad 我个人从未经历过这种情况,但我没有使用Visual Studio。GCC和Clang通常在完全完成之前不会输出对象文件的最终文件名 - 在此之前,我猜它会保存为临时文件或其他形式。 - Muzer

与其他答案相反,我建议您在进行此操作时禁用交换空间。虽然交换空间可以使系统以可预测的方式运行,并且通常用于增加访问磁盘的应用程序的吞吐量(通过将未使用的页面驱逐到为磁盘缓存腾出空间),但在这种情况下,听起来您的系统因为太多活跃使用的内存被强制转移到交换空间而变得无法使用。
我建议在执行此任务时完全禁用交换空间,这样当RAM填满时,内存不足杀手将立即采取行动。
替代解决方案:
提高交换分区的读取速度,将交换分区放在RAID1中。
或者如果你感觉冒险的话,可以使用RAID0,但是如果任何一个磁盘出现故障,会导致大量正在运行的程序崩溃。
减少并发构建作业的数量(我们都说“更多核心=更快”,却忘记了这对RAM产生线性负担)。
这可能有两种结果,但是尝试在内核中启用zswap。它在将页面发送到交换分区之前对其进行压缩,这可能为您的机器提供足够的空间来加快速度。另一方面,它可能只会增加额外的压缩/解压缩负担,成为阻碍。
降低优化级别或使用不同的编译器。优化代码有时会占用数GB的内存。如果打开了LTO,链接阶段也会使用大量RAM。如果其他方法都失败了,可以尝试使用轻量级编译器(例如tcc)编译项目,虽然会稍微降低编译产品的运行性能,但通常在开发/调试目的下是可以接受的。

我在做什么? - Anon
在你编写项目的时候,或者如果你经常进行编译,甚至是在一般的开发过程中。 - Score_Under
内存不足杀手会在RAM填满时立即启动。这种情况从未发生过,我从来没有遇到过。我曾经让电脑连续运行一整夜,第二天它们还是和我离开前几个小时一样僵硬。可能取决于应用程序吧? - Anon
6如果你关闭了交换空间(swap),那是Linux在内存耗尽时的行为。如果Linux没有调用内存不足杀手(out-of-memory killer)而是冻结,那可能意味着设置存在更深层次的问题。当然,如果交换空间打开了,行为会稍有不同。 - Score_Under
10@Akiva 你试过不使用交换空间吗?这个答案非常准确。我想补充一点,当你已经陷入困境时,运行sudo swapoff -a可能会拯救你:它会立即停止对交换空间的任何额外使用,即OOM killer应该在下一瞬间被调用并使机器恢复正常。当调试内存泄漏或编译例如firefox时,sudo swapoff -a也是一个很好的预防措施。通常情况下,交换空间有一定用处(例如休眠或交换出真正不需要的东西),但当你实际使用内存时,冻结问题会更严重。 - Jonas Schäfer
@JonasWielicki:太棒了。我原以为swapoff会拒绝工作,或者在系统试图换入任何可用的页面(并驱逐由文件支持的只读页面)时触发更多的抖动,当一个失控的进程驱逐其他所有人的页面时。我没有想到它会在下一次需要更多页面时触发OOM killer。 - Peter Cordes
2@Score_Under:在每个磁盘上单独设置交换分区比在md raid0设备上设置交换分区要高效得多。我忘记在哪里读到的了。 Linux RAID维基推荐使用单独的分区而不是raid0,但并没有明确说明为什么这样做更好。总之,对于交换分区来说,RAID1或RAID10n2是有意义的,特别是如果你只是想将一些脏但非常冷的页面交换出去,以便为页面缓存留更多的RAM。也就是说,交换性能并不是一个大问题。 - Peter Cordes
在一台内存有限的计算机上,在编译过程中禁用交换空间是导致系统崩溃的一种确定方法。 - Dmitry Grigoryev
@DmitryGrigoryev 是的,程序会在没有警告的情况下退出 - 这是因为Linux的OOM killer - 但这远比系统无法回复地锁死要好得多。 - Score_Under
2我的观点是,按照你的建议去做,可能根本无法运行那些程序,因为它们需要交换空间。一个每次都失败的构建比一个有50%机会让系统死机的构建更糟糕,不是吗? - Dmitry Grigoryev
1但失败的原因相当明显 - 就是你造成的 - 而且在那时你可以做出有根据的决定,是否重新启动它。 - Riking
2没有交换,许多机器无法编译大量的代码块。为什么你会认为他想要牺牲的是编译呢? - David Schwartz
2@DavidSchwartz 有时候会让人惊讶的是,某个过程需要如此大量的内存。一旦了解到这一点(最好以合理的方式发现,即崩溃编译而不是完全锁定计算机,可能会因此丢失其他进程中的宝贵数据),就可以通过关闭浏览器、邮件客户端和其他与编译无关的软件来释放更多内存,并以受控的方式进行。使用交换空间和糟糕的I/O调度,你只会得到一个冻结状态,很难从中恢复。 - Jonas Schäfer

你可以使用以下命令(如有需要,可重复执行)来终止占用系统内存最多的进程:
ps -eo pid --no-headers --sort=-%mem | head -1 | xargs kill -9

使用以下命令:

  • ps -eo pid --no-headers --sort=-%mem:显示所有正在运行的进程的进程ID,按内存使用量排序
  • head -1:只保留第一行(使用最多内存的进程)
  • xargs kill -9:终止该进程

在Dmitry准确的评论之后进行编辑:

这是一个快速而简单的解决方案,应该在没有运行敏感任务(即您不希望使用kill -9命令终止的任务)时执行。


6这比让OOM killer处理情况要糟糕得多。OOM killer比那聪明多了。你真的在正在编译的计算机上运行这样的命令吗? - Dmitry Grigoryev
@DmitryGrigoryev,有时候在我的桌面上关闭Xorg真是太聪明了。在现代内核中,OOMK似乎变得更加稳定了,但毕竟经历了那么多,我并不真的信任它。 - Ruslan

在运行资源消耗命令之前,您还可以使用setrlimit(2)系统调用,可能与您的bash shell中的ulimit内置(或zsh中的limit内置)一起使用,特别是使用-v来设置RLIMIT_AS。然后,太大的虚拟地址空间消耗(例如使用malloc(3)使用的mmap(2)sbrk(2))将失败(并且errno(3)将为ENOMEM)。
然后他们(即您在shell中键入ulimit后挨饿的进程)会在冻结您的系统之前被终止。
还可以阅读Linux Ate My RAM,考虑禁用内存过度承诺(通过以root身份运行命令echo 0 > /proc/sys/vm/overcommit_memory,参见proc(5)...)。

这种情况经常发生在我编译软件的时候。在这种情况下,你可以尝试使用类似"killall -9 make"的命令(或者你用来管理编译的其他命令)。这将停止编译进程,并发送SIGHUP信号给所有从该进程启动的编译器进程(希望它们也会停止)。此外,这个命令不需要sudo权限,假设你以与登录用户相同的身份进行编译。由于它只终止导致问题的实际原因,而不是你的网络浏览器、X会话或随机的某个进程,所以它不会干扰你在系统上进行的其他操作。

2真是可惜,我不得不滚动这么远才找到这个答案。我希望有人能提出一种暂停这个吃内存的进程的方法。 - TOOGAM
这并不是OP所期望的答案,但它确实以文字回答了问题:当我在糟糕的机器上构建时,我的糟糕的机器变得无法使用 - 别在糟糕的机器上构建。 - 9ilsdx 9rvj 0lo

为自己创建更多的交换空间。

以下将增加8G的交换空间:

dd if=/dev/zero of=/root/moreswap bs=1M count=8192
mkswap /root/moreswap
swapon /root/moreswap

它仍然会很慢(你在交换),但实际上不应该用完。 现代的Linux版本可以将数据交换到文件中。如今,交换分区的唯一用途就是让笔记本电脑进入休眠状态。


1我已经将这个方法实现为一个脚本,实际上,在这里(https://askubuntu.com/a/931170/295286)。非常有用,可以动态添加交换空间。 - Sergiy Kolodyazhnyy
注意,创建交换文件只适用于某些文件系统。例如,BTRFS不支持交换文件,而Ext4支持。 - Anon
7一些交换空间通常是明智的,但是分配大量的交换空间只会让机器在OOM killer介入并选择一个志愿者之前更加疲惫不堪。关于“将交换空间设置为内存的两倍”的老措辞早已过时。就个人而言,我认为分配超过约1 GB的交换空间没有任何价值。 - Criggie
5使用ext4文件系统,您可以使用fallocate -l 8G /root/moreswap而不是dd来避免在系统繁忙时进行8GB的I/O操作。然而,这种方法在其他文件系统中并不适用,尤其是XFS文件系统,因为swapon命令会将未写入的范围视为空洞。(我猜测这个xfs邮件列表讨论没有取得成果)。另请参考swapd,这是一个守护进程,可以动态创建/删除交换文件以节省磁盘空间。还可以查看https://askubuntu.com/questions/905668/does-ubuntu-support-dynamic-swap-file-sizing - Peter Cordes
但是在具有合理数量的RAM和磁盘空间的现代桌面上,这可能没有用。这只会使得在一个有缺陷的程序分配和使用内存时,恢复速度变慢。 - Peter Cordes
@Criggie,@Peter Cordes 这个问题被提出来是一个紧迫的问题。增加交换空间将允许更多的东西适应虚拟内存,但代价是速度会变慢。消耗大量内存并不一定意味着程序失控了,只是说明它需要比你拥有的内存更多的内存。 - William Hay
1@Criggie “就我个人而言,我认为分配超过1GB的交换空间总量没有任何价值”- 你试过构建Firefox吗? - Dmitry Grigoryev
@DmitryGrigoryev 真的吗?火狐浏览器实际上是那么沉重的构建吗? - Anon
1@Akiva 上次我查看的时候,推荐的构建配置是16GB的内存。主要的可执行文件(xul.dll)大约有50MB,所以它比Linux内核重大约10倍。 - Dmitry Grigoryev
1我完全支持@Criggie的观点。如果你的机器内存足够大,最好不要让它在出现故障时一直疯狂运转。如果你需要为某个特定目的增加交换空间,你总是可以临时启用更多的交换分区。 - sudo
@Criggie 你得小心处理过度承诺的设置。 - GnP

一种在短时间内获得一大块免费RAM的方法是使用zram,它创建了一个压缩的RAM磁盘并将数据交换到其中。对于任何半好的CPU来说,这比常规的交换要快得多,并且在许多现代的内存占用程序(如Web浏览器)中,压缩率相当高。
假设您已经安装和配置了zram,您只需要运行以下命令:
sudo service zramswap start

这个适用于所有的文件系统,比如btrfs吗? - Anon
1@Akiva zram从不接触磁盘,所以我会说是的 ;) - Dmitry Grigoryev

另外一件事情是通过这个命令来释放内存页面缓存:
echo 3 | sudo tee /proc/sys/vm/drop_caches

kernel.org文档(重点添加)中: "drop_caches" 写入此命令将导致内核清除干净的缓存,以及可回收的slab对象,如目录项和索引节点。一旦被清除,它们的内存就会变为空闲。 要释放页面缓存:echo 1 > /proc/sys/vm/drop_caches 要释放可回收的slab对象(包括目录项和索引节点):echo 2 > /proc/sys/vm/drop_caches 要释放slab对象和页面缓存:echo 3 > /proc/sys/vm/drop_caches 这是一个非破坏性操作,不会释放任何脏对象。为了增加此操作释放的对象数量,用户可以在写入/proc/sys/vm/drop_caches之前运行"sync"。这将最小化系统上的脏对象数量,并创建更多可供清除的候选对象。

有趣...能解释一下那个命令逻辑吗? - Anon
1@Akiva 基本上,这个命令告诉Linux内核释放RAM。但这并不能消除问题的根源,即杀死引起问题的进程,所以Oli的回答才是解决问题的方法。清空缓存将防止系统内存耗尽,从而避免冻结,为您争取时间来找出实际问题。如果您使用的是硬盘而不是固态硬盘,这可能会比创建交换文件快一些。 - Sergiy Kolodyazhnyy
7当内存被填满时,缓存是首先被清空的,所以我不认为这会有太大帮助。事实上,我认为除了调试内核行为或优化磁盘访问时间之外,这个命令没有实际用途。我谦卑地建议不要在需要更高性能的任何系统上运行此命令。 - Score_Under
2@Score_Under - "当内存填满时,缓存是第一个被清除的" -- 嗯,这取决于你在/proc/sys/vm/swappiness中的设置。如果将swappiness设置为0,你是对的。默认设置为60时,你接近正确。然而,如果将其设置为200,那么最近未使用的运行进程页面将首先被丢弃... 在这种特殊情况下,这个命令可能会有用。但是,将swappiness设置为0(或一些较低的值,比如20或30)可能是一个更好的通用方法。 - Jules
3@Score_Under 这个命令在旧的内核中非常有用,特别是解决kswapd bug时(有些人甚至创建了定时任务来使用它)。但你说得对,我怀疑它对这个问题没有帮助。 - Dmitry Grigoryev
当你达到可以以这种方式处理事情的水平时,摒弃掉笨拙的"sudo"支撑 :) - rackandboneman
@rackandboneman 你是什么意思? - Sergiy Kolodyazhnyy

sudo swapoff -a会禁用交换空间,当系统内存不足时,内核会自动杀掉得分最高的进程。如果我知道将要运行一些占用大量内存的任务,我会使用这个命令来终止它,而不是让它进入交换空间并永远卡住。之后可以使用sudo swapon -a重新启用交换空间。

稍后,您可能想查看一下交换设置。听起来您的交换空间与根分区位于同一磁盘上,这会在使用交换空间时减慢系统速度,所以如果可以的话应该避免这种情况。此外,在我看来,现代系统通常配置了过多的交换空间。32GiB的RAM通常意味着默认分配了32GiB的交换空间,好像您真的想把32GiB放入交换空间一样。


哦,我刚刚看到有人在上面留言了。 - sudo