内存/提交费用如何在 Windows 10 中工作?

mar*_*ixy 7 memory memory-management windows-10

这个问题是由以下经常观察到的现象引起的,我想找到解释:

  1. 当前提交通常高于物理使用量 + 页面文件大小。那是怎么回事?那应该是不可能的吧?[这似乎是因为压缩。这将问题转换为:为什么不提交限制然后上升或什么?即,如果它对内存使用没有帮助,那么压缩有什么意义?]
  2. 有时这会达到当前提交超过两倍物理内存使用量的极端水平!
  3. 当提交费用填满并且 Windows 开始要求我关闭时,大部分时间物理内存在 60% 左右。这似乎非常低效。

正如 Process Explorer 所报告的那样,这是在 Windows 10 上。

我想回答的最终问题是:我可以放弃人为地将我的页面文件膨胀到我的空间不足 SSD 无法处理的水平,这样我才能真正有效地利用我的物理内存吗?(或者即使它没有那么满。也就是说,我想避免诸如“对您的页面文件执行 X/Y/Z”之类的建议。)

Jam*_*han 13

一旦您了解提交费用仅代表潜在的- 但“保证可用,如果需要” - 使用虚拟内存,而“私有工作集” - 本质上是“提交”内存使用的 RAM,这实际上非常简单-是实际使用,页面文件空间也是如此。(但这并不是 RAM 的全部使用,因为还有其他东西在使用 RAM)。

假设我们谈论的是 32 位系统,因此每个进程可用的最大虚拟地址空间通常为 2 GiB。(对于 64 位系统,以下任何一项都没有实质性区别,除了地址和大小可以更大 - 更大。)

现在假设在进程中运行的程序使用 VirtualAlloc(一种 Win32 API)来“提交”2 MiB 的虚拟内存。正如您所期望的那样,这将显示为额外的 2 MiB 提交费用,并且在此过程中可用于未来分配的虚拟地址空间减少了 2 MiB 字节。

但它实际上不会使用任何物理内存 (RAM)!

VirtualAlloc 调用将返回给调用者分配区域的起始地址;该区域将在 0x10000 到 0x7FFEFFFF 范围内的某个地方,即大约 2 GiB。(从不分配每个进程中 vas 的第一个和最后一个 64KiB,或十六进制的 0x10000。)

但同样 - 还没有实际物理使用 2 MiB 的存储!不在 RAM 中,甚至不在页面文件中。(有一个称为“虚拟地址描述符”的微小结构,它描述了私有提交区域的起始 va 和长度。)

所以你有它!提交费用增加了,但物理内存使用量没有增加。

这很容易用 sysinternals 工具演示testlimit

稍后,假设程序在该区域(与何处无关)存储了一些东西(即内存写入操作)。在任何区域下面还没有任何物理内存,因此这样的访问将导致页面错误。作为响应,操作系统的内存管理器,特别是页面错误处理程序(简称“寻呼机”……它被称为 MiAccessFault),将:

  1. 分配一个以前“可用”的物理页面
  2. 为被访问的虚拟页设置页表条目,将虚拟页号与新分配的物理页号相关联
  3. 将物理页面添加到进程私有工作集
  4. 并消除页面错误,导致引发错误的指令被重试。

您现在已经“错误”了一页 (4 KiB) 到进程中。物理内存使用量会相应增加,“可用”RAM 会减少。提交费用不会改变。

稍后,如果该页面有一段时间没有被引用并且对 RAM 的需求很高,则可能会发生这种情况:

  1. 操作系统从进程工作集中删除页面。
  2. 因为它在被带入工作集后被写入,所以它被放在修改页面列表中(否则它会进入备用页面列表)。页表条目仍然反映了 RAM 页面的物理页号,但现在它的“有效”位已清除,因此下次引用它时将发生页面错误
  3. 当修改页面列表达到一个小的阈值时,“系统”进程中的一个修改页面写入器线程被唤醒并将修改页面的内容保存到页面文件中(假设你有一个),然后......
  4. 将这些页面从修改列表中删除,并将它们放在备用列表中。它们现在被视为“可用”RAM 的一部分;但就目前而言,它们仍然保留了它们各自流程中的原始内容。同样,提交费用不会改变,但 RAM 使用量和进程私有工作集会下降。
  5. 备用列表上的页面现在可以重新调整用途,也就是说用于其他用途- 例如解决系统上任何进程的页面错误,或由 SuperFetch 使用。然而...
  6. 如果一个进程在修改或备用列表中丢失了一个页面,则在物理页面被重新调整用途之前尝试再次访问它(即它仍然具有其原始内容),页面错误将在不从磁盘读取的情况下解决。该页面被简单地放回进程工作集中,并且页表条目被设置为“有效”。这是“软”或“廉价”页面错误的示例。我们说备用列表和修改列表形成了一个系统范围的页面缓存,这些页面可能很快就会再次需要。

如果您没有页面文件,则步骤 3 到 5 更改为:

  1. 这些页面位于修改后的列表中,因为无处可写其内容。

  2. 这些页面位于修改后的列表中,因为无处可写其内容。

  3. 这些页面位于修改后的列表中,因为无处可写其内容。

第 6 步保持不变,因为修改后的列表上的页面可以作为“软”页面错误返回到丢失它们的进程中。但是,如果没有发生这种情况,页面将位于修改后的列表中,直到进程释放相应的虚拟内存(可能是因为进程结束)。

除了私有提交内存之外,还有其他用途的虚拟地址空间和 RAM。有映射的虚拟地址空间,其后备存储是一些指定的文件而不是页面文件。被分页的映射 vas 的页面反映在 RAM 使用中,但映射内存不会对提交费用做出贡献,因为映射文件提供了后备存储:映射区域的任何不在 RAM 中的部分都简单地保存在映射文件。另一个区别是大多数文件映射可以在进程之间共享;一个进程已经在内存中的共享页面可以添加到另一个进程,而无需再次访问磁盘(另一个软页面错误)。

还有不可分页的vas,它没有后备存储,因为它总是驻留在 RAM 中。这对报告的 RAM 使用量和“提交费用”都有贡献。

这似乎是因为压缩。这将问题转换为:为什么不提交限制然后上升或什么?即,如果压缩对内存使用没有帮助,那么压缩的意义何在?

不,它与压缩无关。Windows 中的内存压缩是作为中间步骤完成的,在页面上否则将写入页面文件。实际上,它允许修改后的页面列表使用更少的 RAM 来包含更多的内容,以 CPU 时间为代价,但速度远高于页面文件 I/O(甚至到 SSD)。由于提交限制是根据RAM + 页面文件大小计算的,而不是 RAM 使用量 + 页面文件使用量,因此这不会影响提交限制。提交限制不会随着 RAM 的使用量或用途而改变。

当提交费用填满并且 Windows 开始要求我关闭时,大部分时间物理内存在 60% 左右。这似乎非常低效。

并不是说 Windows 效率低下。这是您正在运行的应用程序。他们投入的 vas 比实际使用的要多得多。

整个“提交费用”和“提交限制”机制的原因是:当我调用 VirtualAlloc 时,我应该检查返回值以查看它是否非零。如果它为零,则意味着我的分配尝试失败,可能是因为它会导致提交费用超过提交限制。我应该做一些合理的事情,比如尝试减少提交,或者干净地退出程序。

如果 VirtualAlloc 返回非零值,即一个地址,它告诉我系统已经做出了保证——如果你愿意的话——承诺——无论我要求多少字节,从那个地址开始,如果我选择访问它们,都将可用;有什么地方可以放一切 - 无论是 RAM 还是页面文件。即没有理由期望在访问该区域内的任何内容时出现任何类型的失败。这很好,因为期望我检查“它有效吗?”是不合理的。每次访问分配的区域时。

“现金借贷银行”类比

这有点像银行提供信贷,但严格按照手头现金的方式提供。(当然,这不是真正的银行的运作方式。)

假设银行开始时手头有 100 万美元现金。人们去银行要求不同数额的信用额度。假设银行批准我获得 100,000 美元的信用额度(我创建了一个私人承诺区域);这并不意味着任何现金实际上已经离开了金库。如果我后来真的借了 20,000 美元的贷款(我访问了该地区的一个子集),那确实会从银行中取出现金。

但是,无论我是否贷款,我已获得最多 10 万美元的批准这一事实意味着银行随后只能为其所有客户再批准价值 90 万美元的信贷额度。该银行将不超过其现金储备(即它不会批准信贷过量使用它们),因为这将意味着银行可能不得不把以前批准的借款人离开时,他们后来出现打算拿出自己的贷款. 那将是非常糟糕的,因为银行已经承诺允许这些贷款,而银行的声誉将一落千丈。

是的,就银行对现金的使用而言,这是“低效的”。客户获批的信贷额度与其实际贷款金额之间的差距越大,其效率就越低。但效率低下并不是银行的错。要求如此高的信用额度却只发放小额贷款是客户的“错”。

该银行的商业模式是,当他们出现以获得贷款时,它根本无法拒绝先前批准的借款人 - 这样做对客户来说是“致命的”。这就是为什么银行会仔细跟踪“承诺”了多少贷款资金。

我想扩展页面文件或添加另一个页面文件就像银行出去获得更多现金并将其添加到贷款基金中。

如果您想在这个类比中对映射和不可分页内存进行建模……不可分页就像一笔小额贷款,您在开户时需要取出并保留。(定义每个新进程的不可分页结构。)映射内存就像带上自己的现金(被映射的文件)并将其存入银行,然后一次只取出一部分(分页)。为什么不一次全部翻页?我不知道,也许你的钱包里没有空间放那么多现金。:) 这不影响别人借钱的能力,因为你存入的现金是在你自己的账户里,而不是一般的贷款基金。这个类比在那里开始分解,特别是当我们开始考虑共享内存时,所以不要把它推得太远。

回到 Windows 操作系统:事实上,你有很多“可用”的 RAM 与提交费用和提交限制无关。如果您接近提交限制,则意味着操作系统已经提交 - 即 承诺在要求时提供 - 那么多存储。不一定要全部使用才能强制执行限制。

我可以放弃人为地将我的页面文件膨胀到我的空间不足 SSD 无法处理的水平,这样我才能真正有效地利用我的物理内存吗?(或者即使它没有那么满。也就是说,我想避免诸如“对您的页面文件执行 X/Y/Z”之类的建议。)

好吧,很抱歉,但如果您遇到提交限制,您只能做三件事:

  1. 增加你的内存。
  2. 增加页面文件大小。
  3. 一次运行更少的东西。

选项 2:您可以在硬盘驱动器上放置第二个页面文件。如果应用程序实际上没有使用所有已提交的内存 - 显然它们没有使用,因为您看到了如此多的可用 RAM - 您实际上不会经常访问该页面文件,因此将其放在硬盘驱动器上不会伤害性能。如果硬盘驱动器的缓慢仍然会困扰您,另一种选择是获得一个小而便宜的第二个 SSD,并将您的第二个页面文件放在上面。一个“showstopper”将是一台无法添加第二个“不可移动”驱动器的笔记本电脑。(Windows 不会让您将页面文件放在可移动驱动器上,就像任何与 USB 连接的东西一样。)

这是我写的另一个答案,它从不同的方向解释了事情。

ps:您询问了 Windows 10,但我应该告诉您,它在 NT 系列的每个版本中都以相同的方式工作,回到 NT 3.1,以及预发布版本。可能已经改变的是 Windows 页面文件大小的默认设置,从 1.5x 或 1x RAM 大小到更小。我相信这是一个错误。

  • +1 这是我希望我写的答案。这就是现代操作系统的工作方式。在 SSD 出现之前这不是问题,因为我们没有太多 RAM,而且我们有很多硬盘空间。现在我们有很多 RAM 而在某些机器上没有那么多的大容量存储空间,拥有足够的分页文件空间再次成为一个问题。将其作为优先事项,以便您的机器可以有效利用 RAM。 (3认同)