Memlab,一款分析 JavaScript 堆并查找浏览器和 Node.js 中内存泄漏的开源框架


目录

  • 安装 Memlab

  • 在 Demo App 中检测泄漏

  • 设置示例 Web App

  • 1. 克隆仓库

  • 2. 运行示例 App

  • 查找内存泄漏

  • 1.创建一个场景文件

  • 2.运行 memlab

  • 3.调试泄漏跟踪

  • 更多


Memlab
 是一款 E2E
 测试和分析框架,用于发现 JavaScript
 内存泄漏和优化机会。

Memlab
 是 JavaScript
 的内存测试框架。它支持定义一个测试场景(使用 Puppeteer API
),教 Memlab
 如何与您的单页应用程序(SPA
)交互,Memlab
 可以自动处理其余的内存泄漏检查:

  • 与浏览器交互并获取 JavaScript
     堆快照

  • 分析堆快照并过滤掉内存泄漏

  • 聚合和分组类似的内存泄漏

  • 生成用于内存调试的保留器跟踪

安装 Memlab

npm install -g memlab
memlab help

在 Demo App 中检测泄漏

使用 Memlab
 检测分离的 DOM
 元素的教程。Demo 源码:

  • https://github.com/facebookincubator/memlab/tree/main/packages/e2e/static/example

设置示例 Web App

当您单击 “Create detached DOMs”
 按钮时,Demo app 会泄漏分离的 DOM
 元素。每次单击都会创建 1024
 个分离的 DOM
 元素,这些元素由 window
 对象引用。

// @nolint

import Link from 'next/link';
import React from 'react';

export default function DetachedDom() {

const addNewItem = () => {
if (!window.leakedObjects) {
window.leakedObjects = [];
}
for (let i = 0; i < 1024; i++) {
window.leakedObjects.push(document.createElement('div'));
}
console.log('Detached DOMs are created. Please check Memory tab in devtools')
};

return (
<div className="container">
<div className="row">
<Link href="/">Go back</Link>
</div>
<br >
<div className="row">
<button type="button" className="btn" onClick={addNewItem}>
Create detached DOMs
</button>
</div>
</div>

);
}

源文件:packages/e2e/static/example/pages/examples/detached-dom.jsx

1. 克隆仓库

要在本地机器上运行 demo
,请克隆 memlab
 github 存储库:

  • https://github.com/facebookincubator/memlab

git clone git@github.com:facebookincubator/memlab.git

2. 运行示例 App

从 Memlab 项目的根目录运行以下命令:

cd packages/e2e/static/example
npm install
npm run dev

这将启动一个示例 Nextjs
 app。让我们通过从浏览器访问 http://localhost:3000 来确保它正在运行:

这里测试的是 Example 1

查找内存泄漏

1.创建一个场景文件

// @nolint
// memlab/packages/e2e/static/example/scenario/detached-dom.js
/**
* 我们要运行的场景的初始 `url`。
*/

function url() {
return "http://localhost:3000/examples/detached-dom";
}

/**
* 指定 memlab 应如何执行您要测试该 action 是否导致内存泄漏的 action。
*
* @param page - Puppeteer's page object:
* https://pptr.dev/api/puppeteer.page/
*/

async function action(page) {
const elements = await page.$x(
"//button[contains(., 'Create detached DOMs')]"
);
const [button] = elements;
if (button) {
await button.click();
}
// 从 memlab 清理外部引用
await Promise.all(elements.map(e => e.dispose()));
}

/**
* 指定 memlab 应如何执行将重置您在上面执行的 action 的 action。
*
* @param page - Puppeteer's page object:
* https://pptr.dev/api/puppeteer.page/
*/

async function back(page) {
await page.click('a[href="/"]');
}

module.exports = { action, back, url };

这个文件在 packages/e2e/static/example/scenario/detached-dom.js

2.运行 memlab

这可能需要几分钟:

cd packages/e2e/static/example
npm run dev # 注意启动 Demo
memlab run --scenario scenarios/detached-dom.js

3.调试泄漏跟踪

对于每个泄漏的对象组,memLab 打印一个具有代表性的泄漏跟踪。

让我们从上到下分解结果:

第 1 部分:浏览器交互面包屑显示了按照我们的场景文件中指定的方式执行的浏览器交互(导航)memlab

  • page-load[6.5MB](baseline)[s1]
     - 初始页面加载时 JavaScript 堆大小为 6.5MB
    baseline
     堆快照将在磁盘上保存为 s1.heapsnapshot

  • action-on-page[6.6MB](baseline)[s2]
     - 单击 “Create detached DOMs”
     按钮后,堆大小增加到 6.6MB

  • revert[7MB](final)[s3]
     - 在离开触发内存泄漏的页面后,该网页最终达到了 7MB。

第 2 部分:泄漏跟踪的总体摘要

  • 1024 leaks
     - 有 1024 个泄漏的对象。example app
     的第 12 行在 for 循环中创建了 1024 个分离的 DOM 对象。

  • Retained size
     - 泄漏对象集群的聚合保留大小为 143.3KB(内存泄漏根据保留跟踪的相似性分组在一起)。

第 3 部分:每个泄漏簇的详细代表泄漏跟踪

泄漏跟踪是从 GC 根(垃圾收集器遍历堆的堆图中的入口对象)到泄漏对象的对象引用链。跟踪显示泄漏的对象为何以及如何在内存中仍然保持活动状态。打破引用链意味着泄漏的对象将不再可以从 GC 根访问,因此可以进行垃圾回收。

通过从原生 Window(即 GC 根)向下逐个跟踪泄漏跟踪,您将能够找到应该设置为 null 的引用(但这不是由于bug 引起的)。

  • map
     - 这是正在访问的对象的 V8 HiddenClass
    (V8 在内部使用它来存储有关对象形状的元信息和对其原型的引用 - 在此处查看更多信息)- 在大多数情况下,这是 V8 实现细节,可以忽略。

    • https://v8.dev/blog/fast-properties#hiddenclasses-and-descriptorarrays

  • prototype
     - 这是 Window
     类的实例

  • leakedObjects
     - 这表明 leakedObjects
     是 Window
     对象的一个属性,大小为 148.5KB
    ,指向一个 Array
     对象。

  • 0
     - 这表明分离的 HTMLDIVElement
    (即当前未连接到 DOM
     树的 DOM
     元素)被存储为leakedObjects
     数组的第一个元素(由于显示所有 1024
     条泄漏痕迹是压倒性的,Memlab
     只打印一个具有代表性的泄漏痕迹。即属性 0
     而不是属性 0->1023
    )

简而言之,从 Window 对象到泄漏对象的泄漏跟踪路径为:

[window](object) -> leakedObjects(property) -> [Array](object)
-> 0(element) -> [Detached HTMLDIVElement](native)

与示例中的泄漏代码匹配:

window.leakedObjects = [];
for (let i = 0; i < 1024; i++) {
window.leakedObjects.push(document.createElement('div'));
}

更多

  • 官网及 Demo

    • https://facebookincubator.github.io/memlab

  • 文档

    • https://facebookincubator.github.io/memlab/docs/intro/

  • Meta Engineering 博客文章

    • https://engineering.fb.com/2022/09/12/open-source/memlab/



免责声明:

1、本站资源由自动抓取工具收集整理于网络。

2、本站不承担由于内容的合法性及真实性所引起的一切争议和法律责任。

3、电子书、小说等仅供网友预览使用,书籍版权归作者或出版社所有。

4、如作者、出版社认为资源涉及侵权,请联系本站,本站将在收到通知书后尽快删除您认为侵权的作品。

5、如果您喜欢本资源,请您支持作者,购买正版内容。

6、资源失效,请下方留言,欢迎分享资源链接

文章评论

0条评论