Meta 开源一款内存泄漏检测神器

661次阅读  |  发布于2年以前

在前端开发过程中,我们可能需要花费大量时间来测试、优化和控制页面加载和交互时间,以及 JavaScript 代码大小。我们可以构建自动化系统来检测这些指标,以便出现问题时,可以迅速采取行动解决这些问题,并防止这些问题出现在生产环境。

相比之下,在管理 Web 浏览器内存方面可能会弱一些,从而导致客户端的内存使用情况和内存不足 (OOM) 崩溃的问题频繁。由于内存泄漏通常不明显,因此它们很少在代码审查中被发现,在开发过程中也很难发现,并且在生产中通常很难找到根本原因。

问题

主流的 JavaScript 运行时都有垃圾收集器,那么内存泄漏是怎么回事呢?可能有几种情况:

如果没有适当的自动化系统和流程来控制内存,那么防止回归的唯一防御措施是专家通过 Chrome DevTools 定期挖掘内存泄漏。

对此,Meta 的工程师们在改善用户体验,优化内存方面做了不少研究,并研发了一个自动检测内存泄漏的 JavaScript 内存测试框架:MemLab。目前 Meta 已将这个框架开源,希望它也能为更大的 JavaScript 社区做同样的事情。

MemLab 的工作原理

MemLab 通过预定义的测试场景运行浏览器并比较和分析 JavaScript 堆快照来发现内存泄漏。这个过程分 6 个步骤:

  1. 浏览器交互:MemLab 使用 Puppeteer 自动化浏览器,在目标页面上查找泄露的对象
  2. 区分堆:当导航到一个页面然后离开它时,该页面分配的大部分内存应该被释放——如果没有,则暗示着内存泄漏;
  3. 细化内存泄漏列表:泄漏检测器进一步结合特定于框架来细化泄漏对象的列表;
  4. 生成 retainer traces:MemLab 遍历堆并为每个泄漏的对象生成保留跟踪;
  5. 聚类保持器痕迹:MemLab 将所有保留器跟踪聚集在一起,并为每个共享相似保留器跟踪的泄漏对象集群显示一个跟踪;
  6. 报告泄漏:定期运行 MemLab,以获得关于记忆回归的连续信号,任何新的回归都会添加到内部仪表板,开发人员可以单击并查看每个内存泄漏的保留跟踪上的对象属性。

MemLab 的功能

MemLab 的功能主要有以下几点:

内存泄漏检测

对于浏览器内存泄漏检测,MemLab 需要开发人员提供的唯一输入是一个测试场景文件,该文件定义如何通过使用 Puppeteer API 和 CSS 选择器覆盖三个回调来与网页交互。MemLab 自动区分 JavaScript 堆、优化内存泄漏并聚合结果。

JavaScript 堆的 Graph-view API

MemLab 支持自定义泄漏检测器作为过滤器回调,应用于目标交互分配的每个泄漏候选对象。泄漏过滤器回调可以遍历堆并决定哪些对象是内存泄漏。

内存断言

Node.js 程序或 Jest 测试也可以使用 graph-view API 来获取其自身状态的堆图视图,进行自内存检查,并编写各种内存断言。

内存工具箱

除了内存泄漏检测,MemLab 还包括一组内置的 CLI 命令和 API,用于寻找内存优化机会。

MemLab 的使用案例

在过去的几年中,Meta 使用 MemLab 检测和诊断内存泄漏,并收集了有助于我们优化内存、显着提高内存和可靠性(减少 OOM 崩溃)并改善用户体验的见解。

以下是 MemLab 在 Meta 内部的一个真实案例,一起来看看

React Fiber 节点清理

对于渲染组件,React 构建了 Fiber 树——React 用于渲染虚拟 DOM 的内部数据结构。虽然 Fiber 树看起来像一棵树,但它是一个双向图,将所有 Fiber 节点、React 组件实例和关联的 HTML DOM 元素强连接起来。理想情况下,React 维护对组件 Fiber 树的根的引用,并防止 Fiber 树被垃圾收集。当一个组件被卸载时,React 会断开组件的主机根与 Fiber 树的其余部分之间的连接,然后这些部分可以被垃圾收集。

强连接图的缺点是,如果有任何外部引用指向图的任何部分,则无法对整个图进行垃圾收集。例如,以下 export 语句在模块范围级别缓存 React 组件,因此永远不会释放关联的 Fiber 树和分离的 DOM 元素。

export const Component = (( 
  <List> ... </List> 
): React.Element<typeof List>);

保持活动状态的不仅仅是 React 数据结构。Hooks 和它们的闭包也可以让各种其他对象保活。这意味着单个 React 组件泄漏可能会导致页面对象的重要部分泄漏,从而导致巨大的内存泄漏。

为了防止 Fiber 树中内存泄漏的级联效应,我们添加了一个树的完整遍历,当组件在 React 18 中卸载时会进行清理。这允许垃圾收集器在清理未挂载的树方面做得更好。任何意外的内存泄漏都会限制为较小的泄漏大小。此修复程序将 Facebook 上的平均内存使用量减少了近 25%,我们看到其他使用 React 的站点在升级时有了很大的改进。我们担心这种激进的清理可能会减慢 React 中的卸载速度,但令人惊讶的是,由于内存减少,我们看到了显着的性能提升。

使用

要使用 MemLab,只需要通过 npm 来安装

npm i -g memlab

同时 Meta 也提供了大量的文档和入门指南 https://facebookincubator.github.io/memlab/docs/getting-started,还等什么,用起来试试吧。

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8