我拥有的第一台电脑配备了 128 KiB 的 RAM,直到今天,我仍然对应用程序可能耗尽内存的想法感到震惊,即使是 15 年前的机器也通常配备了 4 GiB 的内存。然而,这是用户遇到不稳定性的最常见原因之一,对于 Firefox 来说,这是 Windows 上崩溃的最大来源。
因此,在 Mozilla,我们投入了大量资源来减少 Firefox 的内存使用量,并仔细监控这些变化。我们还投入了一些额外的精力在 Windows 平台上,因为 Firefox 在 Windows 上比在 macOS 或 Linux 上更容易耗尽内存。然而,这些努力都没有我们部署在 Firefox 105 中的一个酷炫技巧的影响大。
但首先,要理解为什么运行在 Windows 上的应用程序比其他操作系统更容易耗尽内存,重要的是要理解 Windows 如何处理内存。
所有现代操作系统都允许应用程序分配地址空间的块。最初,这些块只代表没有物理内存支持的地址范围,除非数据存储在其中。当应用程序开始使用其保留的地址空间的一部分时,操作系统会分配一块物理内存来支持它,如果需要,可能会交换掉一些现有的数据。Linux 和 macOS 都以这种方式工作,Windows 也是如此,但它比其他操作系统需要额外的步骤。
在应用程序请求一块地址空间后,它需要提交该空间才能使用它。提交一个范围要求 Windows 保证它总是可以找到一些物理内存来支持它。此后,它的行为与 Linux 和 macOS 一样。因此,Windows 将可以提交给应用程序的内存限制为机器的物理内存加上交换文件的大小。
这种资源 - 被称为提交空间 - 是应用程序的硬限制。一旦达到此限制,内存分配就会开始失败。在操作系统术语中,这意味着 Windows 不允许应用程序过度分配内存。
这个系统的一个有趣方面是,应用程序可以提交它不会使用的内存。即使在相应的区域没有存储数据,并且没有使用物理内存来支持已提交的区域,已提交的内存量仍然会计入限制。当我们开始分析内存不足的崩溃时,我们发现许多用户仍然有大量的物理内存可用 - 有时高达数 GB - 但实际上却耗尽了提交空间。
为什么会发生这种情况?我们并不真正知道,但我们做了一些推测:Firefox 会跟踪它使用的所有内存,我们可以解释我们直接提交的所有内存。
但是,我们无法控制 Windows 系统库,尤其是图形驱动程序。我们注意到的一件事是,图形驱动程序会提交内存,以便为系统内存中的纹理腾出空间。这使它们能够在 GPU 内存不足的情况下将纹理从 GPU 内存中交换出来,并将其保留在系统内存中。这是一种机制,类似于在没有足够的 RAM 可用时如何将常规内存交换到磁盘。实际上,这种情况很少发生,但这些区域仍然会计入限制。
我们无法直接解决这个问题,但我们仍然有一个杀手锏:当应用程序在 Windows 上耗尽内存时,它不会被操作系统直接杀死,它的分配只是失败,然后它可以自行决定如何处理。
在某些情况下,Firefox 可以处理分配失败,但在大多数情况下,没有合理或安全的方式来处理错误,它需要以受控的方式崩溃......但如果我们能从这种情况中恢复呢?当交换文件几乎满时,Windows 会自动调整交换文件的大小,从而增加可用的提交空间。我们可以利用这一点吗?
事实证明,答案是肯定的。因此,我们调整了 Firefox 在崩溃之前等待一小段时间,然后重试失败的内存分配。这会导致一些卡顿,因为浏览器可能会卡顿一小部分时间,但这比崩溃好多了。
还有一个角度:Firefox 由多个进程组成,可以承受除主进程之外的所有进程的丢失。延迟主进程崩溃可能会导致另一个进程死亡,如果内存很紧张。这是好事,因为它会释放内存,让我们恢复执行,例如通过删除一个内存消耗过大的网页 。
如果一个内容进程死亡,我们需要重新加载它;如果它是 GPU 进程,浏览器会短暂闪烁,然后重新启动它;无论哪种方式,结果都比浏览器完全崩溃不那么令人不快。我们在之前的 Firefox for Android 和 Firefox OS 中使用了类似的技巧,它在这两个平台上都运行良好。
这个小技巧在 Firefox 105 中发布,对 Firefox 在 Windows 上的稳定性产生了巨大的影响。下面的图表显示了用户在每次活动使用小时内遇到的内存不足的浏览器崩溃次数
您看到的是崩溃减少了 70% 以上,远远超过了我们最乐观的预测。
而且我们还没有完成!延迟主进程导致标签崩溃的增加幅度较小 - 这对于用户来说也很不愉快,尽管它不像浏览器完全崩溃那样令人恼火 - 所以 我们也在减少这些崩溃。
最后但并非最不重要的是,我们希望通过对提交空间不足和物理内存不足的情况进行不同的响应来改善 Firefox 在低内存场景下的行为,这将减少交换,并有助于缩小 Firefox 的占用空间,以便为其他应用程序腾出空间。
我要特别感谢我的同事 Raymond Kraesig,他实现了这个“技巧”,仔细监控了它的影响,并且正在研究上述改进。
8 条评论