Windows 表示 RAM 耗尽,但仍有 4 GB 可用物理内存

use*_*487 35 memory windows-7

Process Explorer 中系统信息的屏幕截图

此系统信息来自 Process Explorer。仍然有可用的物理内存,但系统显示几乎没有剩余的 RAM。

任务管理器还显示使用了大约 74% 的总 RAM。

自安装 Windows 8.1 以来,计算机具有 4+8=12 GB 的 RAM。我通过将 4 GB 更改为 8 GB 模块来升级它。这可能是问题吗?或者这种行为是否正常,我只是误解了可用物理内存的含义?

Jam*_*han 85

简答

“内存不足”弹出窗口表示您的私有提交内存(一种虚拟内存)已超出限制。并不是说您的 RAM(物理内存)用完了。你有多少可用内存并不重要。拥有大量可用 RAM 不允许您超过提交限制。提交限制是您的RAM(无论是否使用!)加上您当前的页面文件大小的总和 。

相反,“用完”提交限制(主要是创建进程私有虚拟地址空间)不一定使用任何 RAM!但是操作系统不会允许它的创建,除非它知道如果需要的话有一些地方可以存储它。因此,您可以在不使用所有 RAM 甚至大部分 RAM 的情况下遇到提交限制。

这就是为什么你不应该在没有页面文件的情况下运行。请注意,页面文件实际上可能永远不会被写入!但它仍然可以让您避免“内存不足”和“内存不足”错误。

中级答案

Windows 实际上没有关于内存不足的错误消息。你用完的是“提交限制”。

该版本的 Process Explorer 中的“系统”图命名不当。它应该被标记为“提交费用”。(在我的版本中,它被称为“系统提交”。更好,但仍然不完全一致。)在任何情况下,图形的“当前”高度都在文本部分的下方显示为“提交费用”-“ Current”,图形的最大高度代表“Commit Charge”-“Limit”。

“提交费用”是指由页面文件(如果有的话)支持的虚拟地址空间 - 换句话说,如果它不能全部放入 RAM 中,其余部分将进入页面文件。(还有其他类型的 vas 要么由其他文件支持 - 称为“映射” vas - 或者必须一直保留在 RAM 中;后者称为“不可分页”。)“提交限制”是最大“提交费用”可以。它等于您的 RAM 大小加上页面文件大小。

您显然没有页面文件(我可以说,因为您的提交限制等于您的 RAM 大小),因此提交限制只是 RAM 大小。

显然,各种程序 + 操作系统已经使用了几乎所有的最大可能提交。

这与有多少可用或可用的 RAM 没有直接关系。是的,您有大约 4.5 GB 的可用内存。这并不意味着您可以超过提交限制。提交的内存不一定使用 RAM,并且不受可用 RAM 量的限制。

您需要重新启用页面文件 - 使用这么多提交的内容,我建议使用 16 GB 页面文件,因为您不想强制操作系统将这么多内容保留在 RAM 中,并且页面文件在它的情况下效果最佳有很多空闲空间 - 或者添加更多 RAM。多很多。为了获得良好的性能,您需要在 RAM 中有足够的空间用于代码和其他不受页面文件支持的内容(但可以分页到其他文件)。

很长的答案

(但仍然比Windows Internals的内存管理章节短很多......)

假设一个程序分配了 100 MB 的进程私有虚拟内存。这是通过带有“提交”选项的 VirtualAlloc 调用完成的。这将导致“提交费用”增加 100 MB。但是这种“分配”实际上并不使用任何 RAM!RAM 仅在第一次访问某些新提交的虚拟地址空间时使用。

RAM最终如何被使用

(如果有的话)

第一次访问新提交的空间几乎总是一次内存写入(在写入之前读取新分配的私有 vas 几乎总是一个编程错误,因为它的初始内容严格来说是未定义的)。但是读或写,结果,当你第一次接触新分配的 vas 的页面时,是page fault。尽管“错误”这个词听起来很糟糕,但页面错误在虚拟内存操作系统中是完全可以预料的,甚至是必需的事件。

为了响应这种特殊类型的页面错误,寻呼机(操作系统内存管理器的一部分,我有时将其缩写为“Mm”)将:

  1. 分配 RAM 的物理页(理想情况下来自零页列表,但无论如何,它来自 Windows 所谓的“可用”:零页、空闲页或备用页列表,按优先顺序排列);
  2. 填写页表项,将物理页与虚拟页关联起来;最后
  3. 关闭页面错误异常。

之后进行内存引用的代码将重新执行引发页面错误的指令,这次引用将成功。

我们说该页面已“故障”进入进程工作集,并进入 RAM。在任务管理器中,这将显示为进程的“私有工作集”增加一页 (4 KB)。并且可用物理内存减少一页。(后者在繁忙的机器上可能很难注意到。)

注 1:此页错误不涉及从磁盘读取的任何内容。以前从未访问过的已提交虚拟内存页面不会在磁盘上开始;它在磁盘上没有地方可以从中读取。它只是在以前可用的 RAM 页面中“具体化”。从统计上讲,事实上,大多数页面错误都在 RAM 中解决,要么解决其他进程已在 RAM 中的共享页面,要么解决页面缓存 - 备用或修改列表,或者像这样的“需求零”页面。

注 2:这仅需要“可用”中的一页,4096 字节。以前从未接触过的提交地址空间通常一次只实现一个页面,因为每个页面都是第一次“接触”。一次做更多的事情不会有任何改进,也没有优势;它只需要n倍的时间。相比之下,当必须从磁盘读取页面时,会尝试一定量的“预读”,因为磁盘读取中的绝大多数时间都在每个操作的开销中,而不是实际的数据传输中。“承诺”的数量保持在 100 MB;一个或多个页面出现故障这一事实并不会减少提交费用。

注 3:假设我们有 4 GB 的“可用”RAM。这意味着我们可以在内存不足之前引用已经分配但从未引用过的提交内存大约一百万次(4 GB / 4096)。此时,如果我们有一个 David Cutler 和 Lou Perazzoli 想要的页面文件,RAM 中一些最长时间前引用的页面将保存在磁盘上,然后可用于解决这些最近的页面错误。(实际上,操作系统会在此之前启动 RAM 回收方法,例如“工作集修整”,并且对页面文件的实际写入被缓存并批处理在修改后的页面列表中以提高效率,并且......)这些都不会影响“承诺”计数。但是,它与“提交限制”有关。如果没有空间容纳所有“

而且它一直在发生......

但是让我们假设我们还没有完成那一百万次以上的引用,并且仍然有大约 4GB 的页面“可用”。现在让我们假设相同的进程 - 或另一个,无关紧要 - 执行另一个 VirtualAlloc,这次是 200 MB 提交。同样,这 200 MB 被添加到提交费用中,并且它不会从可用内存中删除任何 RAM。简单地 VirtualAlloc'ating 地址空间不会使用相应数量的 RAM,并且“可用”RAM 低不会限制 VirtualAlloc 可以使用的地址空间量(高可用 RAM 也不会增加它)。

(好吧,好吧……有一点点开销,相当于每 2 MB 用于页表的一个(可分页!)页面(如果您使用的是 x86 非 PAE 系统,则为 4 MB)分配的虚拟地址空间,并且每个虚拟连续的分配范围都有一个几十字节的“虚拟地址描述符”。)

通过这种方式,这是可能的——而且很常见!- 在只使用少量 RAM 的同时消耗大量“提交费用”。

那么,如果“提交”虚拟地址空间没有用完 RAM,那为什么还要有限制呢?

因为“提交费用”确实代表了未来存储空间的潜在使用。“提交限制”表示可用于保存此类分配的总存储量(RAM + 页面文件空间),如果它们确实被引用并需要存储在某个地方。

当 Mm 批准 VirtualAlloc 请求时,它承诺——“作出承诺”——所有后续对分配区域的内存访问都会成功;它们可能会导致页面错误,但所有错误都可以解决,因为有足够的存储空间来保存所有这些页面的内容,无论是在 RAM 中还是在页面文件中。Mm 知道这一点,因为它知道有多少存储空间(提交限制)以及已经“提交”了多少(当前提交费用)。

(但所有这些页面不一定都被访问过,因此在任何给定时间,不一定有实际的存储量与提交的数量相匹配。)

那么......“系统内存不足”怎么办?

如果您尝试 VirtualAlloc 并且当前提交费用加上请求的分配大小将使您超过提交限制,并且操作系统无法扩展页面文件以增加提交限制......你会得到“内存不足”弹出 - up,进程看到 VirtualAlloc 调用失败。大多数程序会在那时举手而死。有些人会盲目地继续,假设调用成功,然后当他们尝试引用他们认为分配的区域时失败。

再次(抱歉重复):您有多少可用 RAM 并不重要。操作系统已承诺 RAM 或页面文件空间在需要时可用,但该承诺不会从“可用”中减去。可用 RAM 仅在首次引用时被提交的 vm 用完,这就是导致它“故障”的原因……即在物理内存中实现。并且简单地提交(= 分配)虚拟内存并不能做到这一点。它只占用空闲的虚拟地址空间并从中生成可用的虚拟地址空间。

但在“出内存不足”的情况下有许多人对提交的内存分配请求,操作系统中加入了当前承诺费这一neew请求的大小...,发现一共是比提交限制。因此,如果操作系统批准了这个新空间,并且在此之后引用了所有空间,则不会有任何真正的地方(RAM + 页面文件)来存储它们。

操作系统不允许这样做。在最坏的情况下,它不会允许分配的 vas 超过它所拥有的空间 - 即使所有这些都“出错”。这就是“提交限制”的目的。

我告诉你三遍 我告诉你三遍 我告诉你三遍: “可用”RAM 的数量并不重要。提交的虚拟空间实际上还没有使用所有的存储空间,这并不重要。Windows 不能“提交”到虚拟分配,除非将来它“可以”全部出错。

请注意,还有另一种类型的 vas 称为“映射”,主要用于代码和访问大型数据文件,但不收取“提交费用”,也不受“提交限制”的限制。这是因为它带有自己的存储区域,即“映射”到它的文件。“映射” vas 的唯一限制是映射文件的磁盘空间量,以及进程中将它们映射到的可用 vas 量。

但是当我查看系统时,我还没有达到提交限制?

这基本上是一个测量和记录保存问题。在 VirtualAlloc 调用已经尝试并失败后,您正在查看系统。

假设您只剩下 500 MB 的提交限制,并且某些程序尝试使用 VirtualAlloc 600 MB。尝试失败。然后您查看系统并说:“什么?还有 500 MB 剩余空间!” 事实上,到那时可能还剩下很多,因为到那时所讨论的进程可能已经完全消失了,所以它之前分配的所有已提交内存都已被释放。

问题是您无法及时回顾并查看在进行分配尝试时提交的费用多少。而且您也不知道尝试占用了多少空间。因此,您无法确定尝试失败的原因,或者需要多少“提交限制”才能使其工作。

我见过“系统内存不足”。那是什么?

如果在上述情况下操作系统可以扩展页面文件(即您将其保留为默认的“系统管理”设置,或者您管理它但您将最大值设置为大于初始值,并且有足够的可用磁盘空间),并且这种扩展增加了足够的提交限制,让 VirtualAlloc 调用成功,然后…… Mm 扩展页面文件,VirtualAlloc 调用成功。

那就是当您看到“系统内存不足”时。这是一个早期警告,如果事情在没有缓解的情况下继续进行,您可能很快就会看到“内存不足”警告。是时候关闭一些应用程序了。我会从你的浏览器窗口开始。

你认为这是一件好事吗?页面文件扩展是邪恶的!!!

不,不是。看,操作系统并没有真正“扩展”现有文件。它只是分配一个新的范围。效果很像任何其他非连续文件。旧的页面文件内容保持原样;他们不必被复制到一个新的地方或类似的东西。由于与页面文件大小相比,大多数页面文件 IO 处于相对较小的块中,因此任何给定传输跨越范围边界的机会非常罕见,因此除非碎片真的过多,否则碎片不会造成太大伤害。

最后,一旦在扩展中“提交”空间的所有进程都退出(如果不是更早,则在操作系统关闭时),范围将被静默释放并且页面文件将恢复到其先前的大小和分配 - 如果它之前是连续的,它又是这样。

因此,允许页面文件扩展就像一个完全免费的安全网:如果您允许它但系统从不需要它,系统将不会像通常声称的那样“不断扩展和收缩页面文件”,因此它不会产生任何费用。如果您确实需要它,它将使您免于因“虚拟内存不足”错误而崩溃的应用程序。

但是但是但是...

我在许多网站上读到过,如果您允许扩展页面文件,Windows 将不断扩展和收缩页面文件,这将导致页面文件碎片化,直到您对其进行碎片整理。

他们只是错了。

如果您从未见过“内存不足”(或在旧版本中为“虚拟内存不足”)弹出窗口,则操作系统从未扩展过您的页面文件。

如果您确实看到了该弹出窗口,则表明您的初始页面文件大小太小。(我喜欢将它设置为观察到的最大使用量的 4 倍左右;即“%pagefile usage peak” perfmon 计数器应该低于 25%。原因:页面文件空间的管理方式与任何其他堆一样,并且在大量可用空间的情况下效果最佳来玩。)

但他们为什么不只是...

有人可能会争辩说,如果没有可用 RAM 来解决页面错误,操作系统应该让分配发生,然后让引用失败。换句话说,在上面我们描述了初始页面错误如何工作的地方,如果“分配可用的 RAM 物理页面”(步骤 1)因为没有可用的内存而无法完成怎么办,并且没有地方离开页面任何内容以提供任何内容?

然后寻呼机将无法解决页面错误。它必须允许将异常(页面错误)报告回故障线程,可能更改为其他一些异常代码。

设计理念是,如果超出提交限制,VirtualAlloc 将返回零(技术上为 NULL 指针)而不是地址,并且期望程序员知道 VirtualAlloc 调用可能失败是完全合理的。所以程序员应该检查这种情况并做一些合理的回应(比如给你一个机会把你的工作保存到那个点,然后“优雅地”结束程序)。(程序员:您确实检查了从 malloc、new 等返回的 NULL 指针,是的?那您为什么不从这里检查?)

但是程序员不应该期望像这样一个简单的内存引用

i = 0;             // initialize loop counter
Run Code Online (Sandbox Code Playgroud)

可能会失败 - 如果它位于成功提交的地址空间区域中,则不会。(或者映射的地址空间,就此而言。)但是如果遵循“允许过度分配的分配,让内存引用失败”的理念,就会发生这种情况。

不幸的是,像上面代码行那样的内存引用没有一种方便的方法来返回错误状态!他们只是应该工作,就像加法和减法一样。报告此类故障的唯一方法是作为例外。因此,要处理它们,程序员必须将整个程序包装在异常处理程序中。(尝试......捕捉和所有这些。)

那是可以做到的……但是处理程序很难知道如何“做正确的事情”来响应这些异常,因为代码中可能会出现很多很多点。(具体来说,它们可能在每次对 VirtualAlloc 的内存、使用 malloc 或 new 分配的内存......以及所有局部变量的内存引用时出现,因为堆栈也是 VirtualAlloc 的。)

简而言之,在这些情况下让程序优雅地失败将是非常困难的。

另一方面,检查从 VirtualAlloc(或 malloc 或 new,就此而言,尽管它们不是完全相同的事情)返回的 NULL 指针很容易,然后做一些合理的事情......就像不尝试去并做任何程序需要该虚拟空间的事情。并且可能询问用户他们是否想保存到目前为止的工作,如果有的话。(当然,太多的应用程序甚至都懒得做那么多。)

commit 的其他用户

顺便说一句,“提交限制”不会因操作系统的各种分配而减少,例如分页和非分页池、PFN 列表等;这些只是在发生时被指控承担责任。提交费用或提交限制也不受视频 RAM 甚至视频 RAM“窗口”大小的影响。

自己测试一下

您可以使用来自 SysInternals 站点的 testlimit 工具来演示所有这些。选项 -m 将分配提交的地址空间但不会“触及”它,因此不会导致分配 RAM。而选项 -d 将分配并引用页面,导致提交费用增加和可用 RAM 减少。

参考

Russinovich、Solomon 和 Ionescu 的Windows Internals。甚至还有演示允许您使用 testlimit 工具证明所有这些要点。但是,我必须警告您,如果您认为这很长,请注意:仅 Mm 一章就有 200 页;以上是一个极其简化的版本。(另请浏览简介中的“致谢”部分。)

另请参阅MSDN VirtualAlloc 文档

  • @AcePL - 用户实际上并未耗尽内存。他的虚拟内存用完了。这个解释是100%有效的。 (5认同)
  • @AcePL 我解释了原因。简而言之:这是因为他的提交限制对于他的工作量来说太低了。提交限制是总 RAM 大小 + 当前页面文件大小。“可用”RAM 的数量不会进入其中。“提交限制”机制将不允许分配超出物理存储可用性(RAM + 磁盘上的后备存储,即页面文件)的虚拟地址空间来保留内容。请记住,即使它是虚拟的,一旦第一次出现故障,它也必须保存在某个地方。 (4认同)
  • 可能感兴趣:“如果没有可用的 RAM 来解决页面错误,就让分配发生,然后让引用失败”类似于 Linux 默认所做的:它允许一定量的过度使用,但在某些时候它'将耗尽脏页的虚拟内存,这就是内存不足杀手开始的时候......并杀死[这可能是一个完全不相关的进程](http://unix.stackexchange.com/questions/153585/ how-oom-killer-decides-which-process-to-kill-first)。是的,恶心。 (3认同)