电脑在RAM几乎满的情况下会冻结,可能是磁盘缓存问题。

我认为这个问题与this主题有些相似。
无论我启用还是禁用交换空间,只要实际使用的内存接近最大值并且几乎没有剩余空间用于磁盘缓存时,系统就会完全无响应。
硬盘会疯狂旋转,有时在长时间等待10-30分钟后会解冻,有时不会(或者我失去耐心)。有时如果我迅速采取行动,可以慢慢打开控制台并关闭一些占用内存的应用程序,比如浏览器,系统几乎立即解冻。
由于这个问题,我几乎从来没有看到交换空间中的任何内容,只有偶尔会有一些少量的MB,然后不久之后就出现了这个问题。我不太了解,但我猜测可能与磁盘缓存过于贪婪或内存管理过于宽松有关,因此当需要内存时,它不能快速释放,导致系统饥饿。
如果处理大文件(500MB+),加载到磁盘缓存中,然后系统显然无法快速卸载它们,问题会很快出现。
非常感谢任何帮助或想法。
现在我必须时刻生活在恐惧中,当我做一些电脑操作时,它经常会卡住,我通常不得不重新启动。如果内存真的快用完了,我更希望它能够关闭一些用户空间的应用程序,比如浏览器(最好是我可以标记哪些应用程序先关闭)。
虽然奇怪的是为什么交换空间在这种情况下不能帮助我。
更新: 有段时间它没有卡住,但现在又出现了几次。我现在一直在屏幕上监控内存使用情况,当系统卡住时,它仍然显示大约30%的可用内存(可能是被磁盘缓存使用了)。其他症状:如果我在观看视频(VLC播放器)时,声音会先停止,几秒钟后图像也会停止。当声音停止时,我仍然可以对电脑进行一些操作,但当图像停止时,我甚至无法移动鼠标,所以我等了一段时间后重新启动了电脑。顺便说一下,这并不是在我开始观看视频时发生的,而是在观看了一段时间(20分钟)后发生的,那时我并没有主动进行其他操作,尽管浏览器和oowrite一直在第二个屏幕上打开。基本上,某个时刻系统就会决定发生一些问题并卡住。
根据评论中的要求,我在卡住后立即运行了dmesg。我没有注意到任何奇怪的事情,但也不知道要寻找什么,所以这是它的内容: https://docs.google.com/document/d/1iQih0Ee2DwsGd3VuQZu0bPbg0JGjSOCRZhu0B05CMYs/edit?hl=en_US&authkey=CPzF7bcC

16这个问题需要更多的关注。我知道有很多年来都有人报告了这些bug。 - n3rd
1@n3rd:这是该错误 - Dan Dascalescu
@Krišjānis Nesenbergs:如果我错了,请纠正我,复制粘贴一个长文件也会导致它卡住。 - Rick2047
谢谢您提出这个问题并找到解决方案。请在更新中添加日期,否则不清楚哪些方法有效,哪些方法无效。我也遇到了同样的问题,一直在检查内存水平,我有16GB的内存,计划升级到32GB,看看是否可以通过这种方式解决问题... - Beto Aveiga
8个回答

为了解决这个问题,我发现你需要将以下设置调整为大约占据你计算机总物理内存的5%-6%,再除以计算机核心数。
sysctl -w vm.min_free_kbytes=65536

请记住,这是一个每核心的设置,所以如果我有2GB的内存和两个核心,那么我计算了1GB的6%,并额外增加了一点以确保安全。
这会强制计算机尝试保持这个数量的空闲内存,并限制缓存磁盘文件的能力。当然,它仍然会尝试缓存它们并立即将其交换出去,所以您可能还应该限制交换操作:
sysctl -w vm.swappiness=5

(100 = 尽可能经常交换,0 = 仅在绝对必要时交换)
结果是Linux不再随机决定在观看时将大约1GB的整个电影文件加载到内存中,并因此导致机器崩溃。
现在有足够的保留空间来避免内存饥饿,这显然是问题所在(因为以前没有像现在这样的冻结)。
经过一天的测试,死机问题已经解决,有时会出现轻微的减速,因为缓存更频繁,但如果不必每隔几个小时重新启动计算机,我可以接受这种情况。
这里的教训是-默认的内存管理只是其中一种用例,并不总是最好的选择,尽管有些人试图提出不同的建议-家庭娱乐Ubuntu应该与服务器配置不同。
你可能希望将这些设置永久保存,可以通过将它们添加到你的/etc/sysctl.conf文件中来实现,方法如下:
vm.swappiness=5
vm.min_free_kbytes=65536

1好的发现,尽量报告关于它的错误,这样问题就能得到更多的关注,希望有人能提出解决方案,不再随机加载整个电影。 - Oxwivi
谢谢,非常详细地解释了我的问题。非常感谢! - odedbd
1嗯,我尝试了几乎所有的方法,只有你的建议让事情改善了。谢谢你。 - vitalii
1如果我没有设置交换分区,那么我应该使用比5-6%更大的空间吗?在这种情况下,设置vm.swappiness也不会有任何作用,是吗? - Jarett Millard
运行时不使用交换空间不应该需要增加min_free_kbytes,因为它只影响内核OOM Killer的触发时间。如果没有交换空间,swappiness是无效的。你可以将swappiness看作是在RAM被需要时牺牲应用程序和文件缓存之间的一个滑块 - 0表示总是牺牲文件缓存,100表示尽量保持文件在缓存中,并将运行中的应用程序推入交换空间(如果需要)。请注意,牺牲文件缓存实际上与交换相同,如果以后需要相同的文件。唯一的区别在于读取块设备的时间点。 - Mikko Rantalainen
3为了加快这个计算过程,您可以使用free --kilo命令获取总内存大小,使用nproc命令获取处理器数量,并且可以在bash中将它们相加,例如echo $((8242745 * 0.06 / 4)) - Migwell
2"[vm.min_free_kbytes]强制计算机尝试保持这个数量的RAM空闲,并在此过程中限制了缓存磁盘文件的能力。" -- 很抱歉打扰了,但这与vm.min_free_kbytes的功能完全无关。它作为一个页面块被保留,以便在系统内存争用较高时轻松进行原子(即填充或杀死/非__GFP_WAIT)分配。在这里提高它可能确实有意义(因为可能这些停顿与系统内存争用有关),但绝对不是根据这个答案中描述的原因。 - Chris Down
关于具有超线程技术的CPU怎么样?我有4个物理核心和8个逻辑核心。 - user690429
1我只是路过来说一声,这个解决方案修复了我在Ubuntu Budgie 19.10上的问题。非常感谢!@noone: 我的CPU有6个核心和超线程技术。我为12个“核心”进行了计算,目前效果非常好。 - TheMSG
哈哈,我花了2500美元买了一台内存超级大的笔记本电脑来解决那个问题,但它并没有解决问题。现在我正在用一台只有700美元的笔记本电脑测试这个解决方案,它比那台花费2500美元的电脑表现得更好(我正在运行两个Ubuntu服务器+几个视频流和许多其他标签页)。 - amir beygi

这是在一个新的Ubuntu 14.04安装中发生的。

在我的情况下,它与提到的sysctl问题无关。

相反,问题在于交换分区的UUID在安装期间与安装后不同。因此我的交换从未启用,我的机器在几个小时使用后会锁定。

solution是检查当前交换分区的UUID

sudo blkid

然后使用sudo nano /etc/fstab命令,将错误的交换分区UUID值替换为blkid报告的正确值。
简单地重新启动以使更改生效,完成。

3非常感谢!我已经为这个极其令人恼火的错误奋斗了将近一年,尝试了所有的方法来修复它。为什么Linux会有这种行为?它似乎应该像没有交换空间一样运行,并调用OOM-killer。相反,它似乎假装有交换空间,但实际上无法进行交换(因为配置不正确)。 - crazy2be
1@crazy2be 这并不是失败,而是无尽的成功。即使没有任何交换空间,Linux 仍然可以将程序和未修改的文件从内存中分页出来,并从磁盘重新读取它们。 - Martin Thornton

对我来说什么都没用!!

所以我写了一个脚本来监控内存使用情况。如果内存消耗超过阈值,它将首先尝试清除RAM缓存。您可以在脚本中配置此阈值。即使如此,如果内存消耗仍然未降至阈值以下,它将按照内存消耗的递减顺序逐个终止进程,直到内存消耗低于阈值。我已将其默认设置为96%。您可以通过更改脚本中变量RAM_USAGE_THRESHOLD的值来进行配置。

我同意终止消耗大量内存的进程并不是完美的解决方案,但与失去所有工作相比,终止一个应用程序要好得多!如果RAM使用率超过阈值,脚本将向您发送桌面通知。它还会在终止任何进程时通知您。

#!/usr/bin/env python
import psutil, time
import tkinter as tk
from subprocess import Popen, PIPE
import tkinter
from tkinter import messagebox
root = tkinter.Tk()
root.withdraw()

RAM_USAGE_THRESHOLD = 96
MAX_NUM_PROCESS_KILL = 100

def main():
    if psutil.virtual_memory().percent >= RAM_USAGE_THRESHOLD:
        # Clear RAM cache
        mem_warn = "Memory usage critical: {}%\nClearing RAM Cache".\
            format(psutil.virtual_memory().percent)
        print(mem_warn)
        Popen("notify-send \"{}\"".format(mem_warn), shell=True)
        print("Clearing RAM Cache")
        print(Popen('echo 1 > /proc/sys/vm/drop_caches',
                    stdout=PIPE, stderr=PIPE,
                    shell=True).communicate())
        post_cache_mssg = "Memory usage after clearing RAM cache: {}%".format(
                            psutil.virtual_memory().percent)
        Popen("notify-send \"{}\"".format(post_cache_mssg), shell=True)
        print(post_cache_mssg)

        if psutil.virtual_memory().percent < RAM_USAGE_THRESHOLD:
            print("Clearing RAM cache saved the day")
            return
        # Kill top C{MAX_NUM_PROCESS_KILL} highest memory consuming processes.
        ps_killed_notify = ""
        for i, ps in enumerate(sorted(psutil.process_iter(),
                                      key=lambda x: x.memory_percent(),
                                      reverse=True)):
            # Do not kill root
            if ps.pid == 1:
                continue
            elif (i > MAX_NUM_PROCESS_KILL) or \
                    (psutil.virtual_memory().percent < RAM_USAGE_THRESHOLD):
                messagebox.showwarning('Killed proccess - save_hang',
                                       ps_killed_notify)
                Popen("notify-send \"{}\"".format(ps_killed_notify), shell=True)
                return
            else:
                try:
                    ps_killed_mssg = "Killed {} {} ({}) which was consuming {" \
                                     "} % memory (memory usage={})". \
                        format(i, ps.name(), ps.pid, ps.memory_percent(),
                               psutil.virtual_memory().percent)
                    ps.kill()
                    time.sleep(1)
                    ps_killed_mssg += "Current memory usage={}".\
                        format(psutil.virtual_memory().percent)
                    print(ps_killed_mssg)
                    ps_killed_notify += ps_killed_mssg + "\n"
                except Exception as err:
                    print("Error while killing {}: {}".format(ps.pid, err))
    else:
        print("Memory usage = " + str(psutil.virtual_memory().percent))
    root.update()


if __name__ == "__main__":
    while True:
        try:
            main()
        except Exception as err:
            print(err)
        time.sleep(1)

将代码保存在一个文件中,比如说save_hang.py。运行脚本的命令如下:
sudo python save_hang.py

请注意,此脚本仅适用于Python 3,并需要您安装tkinter包。您可以通过以下方式进行安装:
sudo apt-get install python3-tk

希望这能帮到你...

太棒了,谢谢!顺便告诉其他人,我把顶行改成了#!/usr/bin/env python3,因为我同时安装了Python2和3,而默认的是2。 - Jeff Ward
2只是回来说明这个脚本的效果非常好!我的台式机系统(没有交换空间...别问为什么)在过去一个月里没有冻结过,而且每次有进程被终止时我都会收到一个方便的通知。 - Jeff Ward
@JeffWard 很高兴能帮上忙 :) - Saim Raza
1我将if条件从仅内存更改为交换和内存(以防止在仍有可用交换空间时终止进程):if psutil.swap_memory().percent >= SWAP_USAGE_THRESHOLD and psutil.virtual_memory().percent >= RAM_USAGE_THRESHOLD,并且经过实验设置了SWAP_USAGE_THRESHOLD = 98 - Michal Skop

我知道这个问题很旧了,但是在我的Acer C720 Chromebook上的Ubuntu(Chrubuntu)14.04中,我遇到了这个问题。我尝试了Krišjānis Nesenbergs的解决方案,有些时候有效,但仍然会偶尔崩溃。
最后,我找到了一个解决方案,通过安装zram而不是使用SSD上的物理交换空间来解决。为了安装它,我只需按照这里的说明进行操作。
sudo apt-get install zram-config

之后,我能够通过修改第21行的/etc/init/zram-config.conf来配置zram交换空间的大小。
20: # Calculate the memory to user for zram (1/2 of ram)
21: mem=$(((totalmem / 2 / ${NRDEVICES}) * 1024))

为了让我的zram大小与我拥有的内存大小相同,我将2替换为1。自从这样做以后,我的系统再也没有冻结或变得不响应了。

只有在无法安装更多RAM的情况下,zram才是一个可行的选择。如果系统在向SSD交换并且没有交换空间时变得非常缓慢,那么zram可能会稍微有所帮助,直到您尝试进行更多操作并且结果与没有交换空间时的情况相同。 - Mikko Rantalainen

我的猜测是你将vm.swappiness设置为一个非常低的值,这会导致内核交换过晚,留下了太少的RAM供系统使用。
你可以通过执行以下命令来查看当前的swappiness设置:
sysctl vm.swappiness

默认情况下,该值设置为60。Ubuntu Wiki建议将其设置为10,但可以根据需要设置更高的值。您可以通过运行以下命令更改它:

sudo sysctl vm.swappiness=10

这将仅对当前会话进行更改,要使其持久化,您需要将 vm.swappiness = 10 添加到 /etc/sysctl.conf 文件中。
如果您的磁盘速度较慢,请考虑购买一个新的。

实际上,减少交换空间确实减少了问题(它发生得更少)。我现在将其保持在5。虽然也许是由于较高的交换空间而引起的另一个问题,因为当它设置为60时,我决定看电影或编辑一个大文件时,整个近1GB的文件被加载到内存中,然后系统立即开始将我正在使用的程序和用户界面本身交换出去。问题是,我认为我理解了交换部分,我想要的是在内存不足时杀死贪婪的用户应用程序,而不是让机器冻结。(最好还能限制缓存中的文件大小) - Krišjānis Nesenbergs
@Krisa:当系统内存(RAM和交换空间)耗尽时,内核会调用oom_kill来终止进程以释放内存。不幸的是,您无法控制目标进程。要手动触发它,请同时按下Alt + SysRq + F键。运行dmesg命令时,您应该可以看到一些相关信息(包括进程名称和ID)。我认为最好购买一个新的、更快速的硬盘,或者升级您的RAM。 - Lekensteyn
3问题是,oom_kill在计算机死机前大约30分钟都没有被调用。另外,至少有没有办法知道哪个进程会首先被杀掉? - Krišjānis Nesenbergs
2我有2GB的内存,硬盘转速为5400rpm。我真的不觉得这是一个如此老旧的系统,因为当我在一个显示器上观看视频并在另一个显示器上浏览20-30个选项卡时,系统会冻结半小时。实际上,如果我能始终访问控制台并终止一些进程,我会感到非常高兴——有没有办法使用户输入和终端具有超高优先级,以便在系统冻结时仍然正常工作? - Krišjānis Nesenbergs
@Krisa:嗯,我一直以来都在笔记本上运行7200转/分钟以上的速度(现在是0转/分钟)。只有当你的内存(包括交换空间和RAM)用尽时,才会调用oom_kill。如果系统正在进行交换操作,这样慢的磁盘可能导致系统无法响应。你最好禁用交换空间(或设置较低的交换性能)并升级你的RAM。打开20-30个标签是相当多的,我通常只有10个标签打开(使用Firefox)。旧的P4台式机上有2GB RAM,笔记本上有8GB RAM。 - Lekensteyn
1无论如何 - 交换和内存的数量有点离题。问题是,即使禁用交换文件,系统仍会长时间无响应,并且之后有时仍会运行程序(因此它设法在某个地方找到内存),而其他时候则会运行oom_killer。系统应该能够判断内存不足并且不允许我运行更多的东西。那么有没有办法停止这些冻结或者将用户输入的优先级设置得很高,以便当它们发生时,我可以切换到控制台并自己终止一些进程? - Krišjānis Nesenbergs
在这种情况下,你能运行dmesg命令吗?如果调用了oom_kill,会显示一个调用跟踪、CPU和内存使用信息以及被终止程序的进程名称+PID。关于优先级,恐怕我对此没有足够的经验。你可能会对进程选项卡上的“Nice”是什么意思感兴趣。 - Lekensteyn

我已经为这个问题苦苦挣扎了很长时间,但现在在我的笔记本上似乎已经解决了。

如果其他答案都对你无效(我试过大部分),可以尝试调整min_free_kbytes的值,在计算机开始交换内存之前(在空闲内存达到最低值之前)腾出更多的RAM空间。

我有16GB的RAM,但很快内存就会变满,然后停止响应10到30分钟,直到一些东西被交换出去。

至少对我来说,将min_free_kbytes的值设置为高于推荐值可以显著加快交换过程。

对于16GB的RAM,请尝试以下操作:

vm.min_free_kbytes=500000

要设置这个值,请查看其他答案,或者直接谷歌一下 :)

我一直在使用一个Ubuntu的SD卡来运行我的一台笔记本电脑,SD卡上有一个小型的ext4存储分区和一个硬盘上的交换文件。当几乎所有的内存都被使用并且swappiness值过低(有时我更愿意让硬盘完全关闭,因为它很吵),Linux的性能往往会急剧下降,以至于只是进入TTY1杀掉Firefox就需要15分钟。
将/proc/sys/vm/vfs_cache_pressure的默认值从100提高到6000似乎可以帮助解决这个问题。不过,内核文档对此表示警告,说道:
Increasing vfs_cache_pressure significantly beyond 100 may have negative
performance impact. Reclaim code needs to take various locks to find freeable
directory and inode objects. With vfs_cache_pressure=1000, it will look for
ten times more freeable objects than there are.

我对这样做的副作用并不完全确定,所以在进行此操作时请小心。

你可能会在vfs_cache_pressure接近10(也就是远低于100)和将min_free_kbytes设置得更高时获得更好的结果。但要注意,如果你将min_free_kbytes设置得太高,内核的OOM杀手将会把所有人都干掉! - Mikko Rantalainen
@MikkoRantalainen 我已经将 min_free_kbytes 调高到262144,并且我观察到降低 vfs_cache_pressure 会产生相反的效果 - 将其降低到100以下会使系统更快地变得无响应。我不确定具体原因。 - Hitechcomputergeek
一般来说,增加vfs_cache_pressure会导致目录项在缓存的文件内容之前被丢弃,结果通常会导致整体性能下降,特别是当值超过100时。如果您能找出重现系统崩溃/挂起的步骤,例如使用Ubuntu Live CD,那么内核开发人员就可以找出根本原因。对我来说,系统挂起是突然发生的,没有任何警告。我最好的猜测是,在OOM Killer释放足够的RAM之前,内核由于OOM而挂起。我现在正在运行min_free_kbytes=100000,admin_reserve_kbytes=250000和user_reserve_kbytes=500000。 - Mikko Rantalainen
我尚未使用上述配置崩溃,即使我设置了swappiness=5和vfs_cache_pressure=20。该系统具有16 GB的RAM和8 GB的SSD交换空间。另一个系统具有32 GB的RAM和零交换空间,但似乎会随机出现相同的问题 - 在系统感觉缓慢之后按下Alt+SysRq+f似乎有所帮助,因此我猜测如果OOM Killer反应足够快,系统就不会挂起。 - Mikko Rantalainen

不要自己调整内核参数了。试试专门为桌面和笔记本设计的linux-zen或linux-ck,它们经过特殊设计(打补丁和调优)。默认的Linux更加注重吞吐量和没有图形界面的服务器。

zen会提供较低的吞吐量,但响应速度更好。根据我的经验,调整默认内核的参数比linux-zen(或liquorix)和linux-ck提供的吞吐量要少。

对于Ubuntu,我认为你需要手动编译和打补丁以使用Con Kolivas的ck patchset来安装linux-ck内核。

你可以使用基于Linux-zen源码的liquorix内核。请查看https://liquorix.net/

以下是安装方法:

sudo add-apt-repository ppa:damentz/liquorix
sudo apt-get update
sudo apt-get install linux-image-liquorix-amd64 linux-headers-liquorix-amd64

如果你坚持使用默认内核,那么禁用交换空间可能是一个更好的选择。