简短答案

简短答案

来自 Process Explorer 的系统信息屏幕截图

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

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

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

答案1

简短答案

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

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

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

中级答案

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

该版本的 Process Explorer 中的“系统”图表名称不太好。它应该被标记为“提交费用”。(在我拥有的版本中,它被称为“系统提交”。更好,但仍然不完全一致。)无论如何,图表的“当前”高度在文本部分下方显示为“提交费用”-“当前”,图表的最大高度代表“提交费用”-“限制”。

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

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

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

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

您需要重新启用页面文件 - 使用这么多已提交的内容,我建议使用 16 GB 的页面文件,因为您不想强迫操作系统将这么多内容保存在 RAM 中,并且如果页面文件有大量可用空间,则效果最佳 - 否则添加更多 RAM。更多。为了获得良好的性能,您需要在 RAM 中留出足够的空间来存放代码和其他不受页面文件支持的内容(但可以分页到其他文件)。

很长的答案

(但仍然比Windows 内部原理...)

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

RAM 最终会被如何使用

(如果有的话)

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

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

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

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

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

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

笔记2:这仅占用“可用”的一页,即 4096 字节。从未触及的已提交地址空间通常一次只实现一页(发生故障),因为每个页面都是第一次“触及”。一次做更多不会有任何改进,也没有任何优势;只会花费 n 倍的时间。相比之下,当必须从磁盘读取页面时,会尝试一定量的“预读”,因为磁盘读取的绝大部分时间都用于每个操作的开销,而不是实际的数据传输。“已提交”的数量保持在 100 MB;一个或多个页面发生故障的事实不会减少提交费用。

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

而且这种事情一直在发生...

但是,假设我们还没有进行那一百万次引用,并且仍然有大约 4GB 的页面“可用”。现在,假设同一个进程(或者另一个进程,无所谓)执行另一个 VirtualAlloc,这次提交了 200 MB。同样,这 200 MB 被添加到提交费用中,并且它不会从可用 RAM 中移除任何 RAM。简单地 VirtualAlloc 地址空间不会占用相应数量的 RAM,并且较低的“可用”RAM 不会限制您可以 VirtualAlloc 的地址空间量(较高的可用 RAM 也不会增加它)。

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

通过这种方式,就有可能——而且很常见!——在仅使用少量 RAM 的情况下消耗大量的“提交费用”。

那么,如果“提交”虚拟地址空间不会耗尽 RAM,为什么必须有限制?

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

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

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

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

如果您尝试 VirtualAlloc,并且当前提交费用加上请求的分配大小将超出提交限制,并且操作系统无法扩展页面文件以增加提交限制……您将看到“内存不足”弹出窗口,并且该过程会看到 VirtualAlloc 调用失败。大多数程序都会在此时放弃并死掉。有些会盲目地继续,认为调用成功,然后在尝试引用它们认为已分配的区域时失败。

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

但在“内存不足”的情况下,存在已提交内存的分配请求,并且操作系统已将当前提交的费用添加到此新请求的大小中……并发现总数是更多的比提交限制要多。因此,如果操作系统批准了这个新请求,此后所有空间均被引用,将没有任何实际位置(RAM + 页面文件)来存储所有内容。

操作系统不会允许这种情况发生。它不会允许分配比其在最坏情况下能够保留的空间更多的 vas - 即使所有 vas 都“发生故障”。这就是“提交限制”的目的。

我告诉你三遍我告诉你三遍我告诉你三遍:“可用” RAM 的数量并不重要。已提交的虚拟空间实际上尚未使用所有存储空间,这也不重要。除非将来“可能”出现故障,否则 Windows 无法“提交”虚拟分配。

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

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

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

假设您只剩下 500 MB 的提交限制,而某个程序试图 VirtualAlloc 600 MB。尝试失败。然后您看着系统说“什么?还剩下 500 MB!”事实上,到那时可能还剩下很多,因为到那时,相关进程可能已经完全消失,因此其之前分配的所有提交内存都已释放。

问题是你无法回顾过去,看看提交的费用是多少曾是在分配尝试时。而且您也不知道尝试分配多少空间。因此,您无法确切地知道尝试失败的原因,或者需要多少“提交限制”才能使其工作。

我见过“系统是低位运行在记忆中”。那是什么?

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

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

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

不是。你看,操作系统实际上并没有“扩展”现有文件。它只是分配一个新的范围。其效果与任何其他不连续的文件非常相似。旧页面文件内容保持原样;它们不必复制到新位置或类似操作。由于大多数页面文件 IO 相对于页面文件大小而言都是相对较小的块,因此任何给定传输跨越范围边界的可能性非常小,因此除非碎片真的过多,否则碎片不会造成太大影响。

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

因此,允许页面文件扩展可以充当完全免费的安全网:如果您允许它但系统从来不需要它,系统就不会像通常声称的那样“不断地扩展和收缩页面文件”,因此它将花费没有什么。如果您确实需要它,它将避免应用程序因“虚拟内存不足”错误而崩溃。

但但但...

我在几十个网站上看到过,如果你允许页面文件扩展,Windows 将不断地扩大和收缩页面文件,这将导致页面文件碎片化,直到你对其进行碎片整理。

他们完全错了。

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

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

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

有人可能会认为,操作系统应该让分配发生,然后让参考如果没有可用的 RAM 来解决页面错误,则失败。换句话说,上面我们描述了初始页面错误的工作原理,如果“分配可用的物理 RAM 页”(步骤 1)无法完成,因为没有可用的 RAM,是否没有地方可以调出任何内容以供使用?

那么分页器将无法解决页面错误。它必须允许将异常(页面错误)报告回出错的线程,可能更改为其他异常代码。

设计理念是,如果超出提交限制,VirtualAlloc 将返回零(技术上为 NULL 指针)而不是地址,并且完全有理由期望程序员知道 VirtualAlloc 调用可能会失败。因此,程序员应该检查这种情况并采取合理的措施(例如,让您有机会保存到那时为止的工作,然后“优雅地”结束程序)。(程序员:您确实会检查 malloc、new 等是否返回 NULL 指针,对吗?那您为什么不这样做呢?)

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

i = 0;             // initialize loop counter

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

不幸的是,像上面代码行中的内存引用没有方便的方法来返回错误状态!它们只是应该工作,就像加法和减法一样。报告此类失败的唯一方法是异常。因此,为了处理它们,程序员必须将整个程序包装在异常处理程序中。(try ... catch 等等。)

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

简而言之,在这些情况下让程序正常失败将非常困难。

另一方面,检查 VirtualAlloc(或 malloc 或 new,尽管它们并不完全相同)返回的 NULL 指针,然后做一些合理的事,这非常容易……例如,不要尝试继续执行程序需要该虚拟空间的任何操作。也许可以询问用户是否要保存迄今为止的工作(如果有的话)。(当然,太多应用程序甚至懒得做那么多。)

commit 的其他用户

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

自己测试

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

参考

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

也可以看看MSDN VirtualAlloc 文档

答案2

也许加起来出色的公认答案

Windows 和大多数程序都假设它们可以根据需要分配尽可能多的(虚拟)内存。这是不应禁用页面文件的主要原因之一,请参阅建议事实2.2我的超级用户问题

我还链接到这个精彩的 serverfault 答案这里清楚地说明了页面文件的工作原理:

许多人似乎认为 Windows 会根据需要将数据推送到页面文件中。例如:某些东西需要大量内存,而 RAM 又不足以满足需求,因此 Windows 在最后一刻开始疯狂地将数据从 RAM 写入磁盘,以便释放 RAM 以满足新的需求。

这是不正确的。幕后还有更多事情发生。一般来说,Windows 维护一个后备存储,这意味着它希望看到内存中的所有内容也存在于磁盘的某个地方。现在,当出现需要大量内存的情况时,Windows 可以非常快速地清除 RAM,因为这些数据已经存储在磁盘上,如果需要,可以调回到 RAM 中。因此可以说,页面文件中的大部分内容也位于 RAM 中;数据先发制人地放置在页面文件中以加快新的内存分配需求。

进一步阅读提供这里

相关内容