Meta开源的MemLab是一款基于Chromium的JavaScript内存泄漏监测工具,专为单页应用(SPA)设计。它通过Headless浏览器自动执行导航、回退操作并对比Heap快照,能精准定位内存泄漏根源。本文详细介绍MemLab的安装方法、工作原理及CLI命令使用,涵盖从全局安装、场景文件编写到内存断言测试的完整流程,帮助开发者在CI/CD中高效捕获并修复内存问题。
一、MemLab简介
上周,Facebook母公司Meta宣布开源MemLab,这是一款基于Chromium内核浏览器的JavaScript应用内存泄漏检测工具。Facebook技术团队指出,应用的性能和功能问题通常会被用户迅速察觉,但内存泄漏却具有隐蔽性。它不会立即报错,却会持续占用大量内存,导致整个Web会话响应迟缓。
为解决这一痛点,Meta开发了MemLab,旨在自动化内存泄漏检测并简化根本原因的定位过程。据官方公告,Meta内部已利用该工具成功遏制了不可控的内存增长,并识别出产品和基础设施中的内存泄漏点及优化空间。目前,MemLab已在GitHub上开源。

Facebook于2020年重构为单页应用(SPA),其大部分渲染与导航逻辑由客户端JavaScript承担。Meta旗下的其他热门Web应用,如Instagram和Workplace,也采用了类似的架构。
这种架构虽然提升了用户交互速度、改善了开发者体验,并提供了更接近原生应用的感觉,但也使得客户端Web应用的状态管理变得复杂,从而增加了有效管理客户端内存的难度。在SPA中,内存泄漏的后果尤为严重,因为用户可能会长时间持续与页面交互,而MemLab正是为这类场景量身定制的。
JavaScript中内存泄漏的情况多种多样。例如,Facebook工程师Liang Gong和Glenn Conner在公告中提到,当向Chrome控制台发送对象时,Chrome会对其建立隐藏引用以防止被垃圾回收。此外,auth0工程师Sebastian Peyrott也曾指出,其他可能导致泄漏或内存无界增长的情况包括:意外使用全局变量、忘记清除定时器或回调,以及持有DOM外部引用等。
尽管Chrome开发者工具提供了检查JavaScript内存行为的基本手段,如时间线视图和配置文件视图,但这些操作不够直观且无法自动化。相比之下,MemLab可以轻松地集成到CI/CD管道中,正如Gong和Conner所介绍的那样。
二、工作原理
MemLab的工作原理是通过预定义的测试场景驱动Headless浏览器,并对JavaScript Heap快照进行差异分析,从而发现内存泄漏。具体流程包括以下步骤:
- 导航至目标页面并返回;
- 查找未被释放的对象;
- 展示泄漏追踪结果。
据悉,MemLab底层使用了Node.js库“Puppeteer”。该库可控制Google Chrome或其他基于Chromium内核的浏览器,并默认以Headless模式运行,便于命令行交互。
Facebook工程师解释称,MemLab的工作逻辑是导航到一个页面然后离开。正常情况下,该页面分配的内存大部分应被释放;若未被释放,则意味着存在极高的内存泄漏可能性。

我们知道,React使用存储在树结构中的名为Fibers的对象来表示内存中的浏览器DOM。据该团队所述,这可能是导致“巨大内存泄漏”的主要原因之一。拥有强连接图的缺点十分显著:若有任何外部引用指向图的任何部分,就无法对整个图执行垃圾回收。
对于浏览器内存泄漏检测,MemLab只需开发人员提供一个测试场景文件。该文件通过重写Puppeteer API和CSS选择器的三个回调函数来定义与网页的交互方式。MemLab会自动对JavaScript Heap进行差异化处理,完善内存泄漏分析,并汇总结果。

MemLab的另一大特性是提供了JavaScript堆的图形视图,并启用了用于检查Heap快照的API。这意味着开发者可以编写执行内存断言的测试,例如声明某个对象将不再存在于内存中。

此外,MemLab还包含一个用于查找重复字符串实例的工具。在某案例中,团队发现字符串占用了70%的堆空间,且其中半数至少有一个重复实例。虽然Chrome、Edge、Firefox等浏览器均附带内存检查工具,但正如一位开发者在Hacker News上所吐槽的那样,这些工具在调试过程中难以精准揪出内存泄漏问题。

最后,MemLab的另一项强大功能是在测试期间作为命令过程的一部分运行。这意味着如果代码中引入了严重泄漏,开发者也能在代码投入生产环境前将其捕获。
除了内存泄漏检测外,MemLab还包含一组用于查找内存优化机会的内置CLI命令和API,并提供如下功能:
- 堆内容分解;
- 监测单个对象的内存使用情况;
- 查找重复的字符串实例。
例如,监测浏览内存泄漏部分UI。

跟踪UI内存泄漏的整个链路。

三、基本使用
3.1 安装与使用
首先,需要全局安装MemLab插件,安装命令如下:
npm install -g memlab
例如下面是找到谷歌Maps中的内存泄漏的例子,我们可以创建一个场景文件来定义如何与谷歌Maps进行交互,比如将其命名为test-google-maps.js。
function url() {
return 'https://www.google.com/maps/@37.386427,-122.0428214,11z';
}
async function action(page) {
await page.click('button[aria-label="Hotels"]');
}
async function back(page) {
await page.click('[aria-label="Clear search"]');
}
module.exports = {action, back, url};
现在使用下面的命令运行上面的js代码,当memlab与web页面进行交互时就会运行内置的泄漏检测器检测内存泄漏。
memlab run --scenario test-google-maps.js
执行结束之后,Memlab就会打印内存泄漏结果,显示每个泄漏对象集群的一个代表性保留跟踪。
MemLab found 46 leak(s)
--Similar leaks in this run: 4--
--Retained size of leaked objects: 8.3MB--
[Window] (native) @35847 [8.3MB]
--20 (element)---> [InternalNode] (native) @130981728 [8.3MB]
--8 (element)---> [InternalNode] (native) @130980288 [8.3MB]
--1 (element)---> [EventListener] (native) @131009888 [8.3MB]
--1 (element)---> [V8EventListener] (native) @224808192 [8.3MB]
--1 (element)---> [eventHandler] (closure) @168079 [8.3MB]
--context (internal)---> [<function scope>] (object) @181905 [8.3MB]
--bigArray (variable)---> [Array] (object) @182925 [8.3MB]
--elements (internal)---> [(object elements)] (array) @182929 [8.3MB]
...
接着,我们就可以通过这些捕获的跟踪信息定位到里面的方法。
当然,我们也可以使用Memlab查看基于从Chromium、Hermes、memlab或任何node.js或electronic.js程序中获取的单个JavaScript堆快照检测到的内存问题。
memlab view-heap --snapshot <PATH TO .heapsnapshot FILE>
然后,我们可以使用对象的id,比如node-id @28173来精确定位特定的堆对象。

当然,Memlab也支持自定义的检漏器,自定义检漏器时需要在场景文件中添加一个filterLeak文档。对于目标交互分配的每个未释放的堆对象(节点)将调用filterLeak。
function filterLeak(node, heap) {
// ... your leak detector logic
// return true to mark the node as a memory leak
};
heap是最终JavaScript堆快照的图形表示。
3.2 堆分析与研究
除了检测内存泄露意外,Memlab还提供了很多其他有用的命令,比如查看某个对象在运行的交互过程中的整个链路。
memlab analyze unbound-object
获取V8/hermes .heapsnapshot文件。
memlab analyze unbound-object --snapshot-dir <DIR_OF_SNAPSHOT_FILES>
使用memlab analyze查看所有内置内存分析。
memlab trace --node-id <HEAP_OBJECT_ID>
3.3 Memlab API
Memlab的npm包支持在浏览器中启动端到端运行并检测内存泄漏。
const memlab = require('memlab');
const scenario = {
url: () => 'https://www.google.com/maps/@37.386427,-122.0428214,11z',
action: async (page) => await page.click('button[aria-label="Hotels"]'),
back: async (page) => await page.click('[aria-label="Clear search"]'),
}
memlab.run({scenario});
3.4 内存断言
Memlab支持在Node.js程序中进行Jest测试,也可以使用图视图API来获得其自身状态的堆图视图,执行自内存检查,并编写各种内存断言。
import type {IHeapSnapshot} from '@memlab/core';
import {config, takeNodeMinimalHeap, tagObject} from '@memlab/core';
test('memory test', async () => {
config.muteConsole = true;
const o1 = {};
let o2 = {};
tagObject(o1, 'memlab-mark-1');
tagObject(o2, 'memlab-mark-2');
o2 = null;
const heap: IHeapSnapshot = await takeNodeMinimalHeap();
//断言函数
expect(heap.hasObjectWithTag('memlab-mark-1')).toBe(true);
//断言函数
expect(heap.hasObjectWithTag('memlab-mark-2')).toBe(false);
}, 30000);
附件:github.com/facebookinc…
以上就是Meta开源JavaScript内存泄漏监测工具MemLab安装使用的详细内容,更多关于JavaScript内存泄漏监测MemLab的资料请关注本站其它相关文章!