使用这个奇怪的技巧来提高 Firefox 的稳定性

我拥有的第一台电脑配备了 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 上的稳定性产生了巨大的影响。下面的图表显示了用户在每次活动使用小时内遇到的内存不足的浏览器崩溃次数

Firefox trick

您看到的是崩溃减少了 70% 以上,远远超过了我们最乐观的预测。

而且我们还没有完成!延迟主进程导致标签崩溃的增加幅度较小 - 这对于用户来说也很不愉快,尽管它不像浏览器完全崩溃那样令人恼火 - 所以 我们也在减少这些崩溃

最后但并非最不重要的是,我们希望通过对提交空间不足和物理内存不足的情况进行不同的响应来改善 Firefox 在低内存场景下的行为,这将减少交换,并有助于缩小 Firefox 的占用空间,以便为其他应用程序腾出空间。

我要特别感谢我的同事 Raymond Kraesig,他实现了这个“技巧”,仔细监控了它的影响,并且正在研究上述改进。

关于 Gabriele Svelto

更多由 Gabriele Svelto 撰写的文章...


8 条评论

  1. Shawn McNaughton

    这...很聪明。有趣的是,Windows 在这里表现得如此含糊...我不知道我喜欢这种行为,还是把它加到“为什么我应该切换到 Linux”的清单中。

    2022 年 11 月 22 日 上午 07:34

  2. JB Dougherty

    喜欢你关于 Firefox 内存管理的文章。

    如果您对 Mac OS 和 Linux 进行了一些研究,我将很感兴趣。我没有确凿的证据,但我看到过导致 MAC OS 陷入停滞的内存问题。话虽如此,我没有收据来证明这一点。更重要的是,我很久以前就不再是开发者了...但我仍然乐于阅读关于 Mac 和 iOS 平台的有趣技术问题。

    2022 年 11 月 22 日 上午 10:47

  3. peter

    自从这个版本号以来,我的 Firefox 在 Linux 上表现得很奇怪。总是更新后,有时重启后,Firefox 无法启动。准确地说:它启动了,但没有打开任何窗口。当我查看任务管理器时,我看到了 3 个 Firefox 进程。现在我杀死了第 3 个进程。然后只有第一个进程保留,窗口立即显示出来。我猜想,没有缺点就没有优点。:)

    2022 年 11 月 22 日 上午 11:05

  4. SoraWithAnS

    太棒了 - 感谢你们的辛勤工作!

    2022 年 11 月 22 日 下午 15:31

  5. Donny

    读起来真的很酷。恭喜取得如此巨大的稳定性提升!

    读完你对 Windows 如何处理内存以及现实生活中后果的描述后...我不禁认为 Windows 的方式有点疯狂。

    微软的方式“更好”有什么好的理由吗?比如,从性能、稳定性或其他角度来看,有什么优势吗?

    2022年11月27日 下午12:43

  6. Miguel Useche

    不错的文章,作为一名操作系统教授,这是一篇非常好的文章,我可以分享给我的学生,让他们了解 Windows 如何使用真实生产案例管理内存。

    2022年11月27日 下午18:57

  7. rv

    有趣的技巧,描述得很不错。
    感谢您的工作!

    2022年11月30日 下午20:26

  8. Gowri Sankar

    > 重新尝试失败的内存分配

    所以,内存分配失败可以延迟重试,并且可能会成功吗?

    2022年12月2日 下午12:50

本文评论已关闭。