Meta开源JavaScript内存泄漏监测工具MemLab安装使用

目录
  • 一、MemLab简介
  • 二、工作原理
  • 三、基本使用
    • 3.1 安装与使用
    • 3.2 堆分析与研究
    • 3.3 Memlab API
    • 3.4 内存断言

一、MemLab简介

上周,Facebook母公司Meta 宣布了开源 MemLab,一个基于 Chromium 的浏览器的 JavaScript 应用程序内存泄漏监测工具。同时,Facebook 技术团队指出:“应用程序的性能和功能正确性问题通常会被用户立即留意到。然而内存泄漏却不一样,它不容易被立即察觉,但它每次都会吃掉一大块内存,使得整个网络会话的响应变得非常慢。”

为了帮助开发人员解决这个问题,Meta 构建了MemLab,它可以自动进行内存泄漏检测并更容易找到泄漏的根本原因。据官方公告称,Meta 内部使用它成功地控制了不可持续的内存增长,并识别了产品和基础设施中的内存泄漏和内存优化机会。目前,Meta 已经在 GitHub 上开源了 MemLab。

Facebook在 2020 年被重新设计为单页应用程序 (SPA),该应用程序的大部分渲染和导航使用客户端 JavaScript。而 Meta 的大多数其他流行网络应用程序都使用了类似的架构来构建,包括 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 snapshots 进行差异分析来发现内存泄漏。要达到这一目的,需要经过如下几步:

  • 导航到页面并返回;
  • 查找未释放的对象;
  • 显示泄露追踪结果。

据悉,MemLab 使用了一个名为“Puppeteer”的 Node.js 库。它可以控制 Google Chrome 或其它基于 Chromium 内核打造的浏览器,且默认情况下以 headless 模式运行(方便命令行交互)。

Facebook 工程师解释称,MemLab 的工作方式就是导航到一个页面、然后离开。正常情况下,可预计该页面分配的大部分内存也将被释放。但若没有被释放,则意味其存在极高的内存泄露可能性。

我们知道,React 使用存储在树结构中、被称作 Fibers 的对象,来表示内存中的浏览器文档对象模型(DOM)。据该团队所述,这可能是存在“巨大内存泄露”的一个主要原因。拥有强连接图的缺点很是显著,若有任何外部引用指向图的任何部分,就无法对整个图开展垃圾回收。

对于浏览器内存泄漏检测,MemLab 需要开发人员提供的唯一输入是一个测试场景文件,该文件定义了如何通过 overriding Puppeteer API 和 CSS 选择器的三个回调来与网页进行交互。MemLab 会自动对 JavaScript heap 进行差异化处理,完善内存泄漏,并对结果进行汇总。

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

此外还有一个用于查找重复字符串实例的工具,在某个案例中,团队发现字符串占用了 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的资料请关注我们其它相关文章!

时间: 2022-09-20

js内存泄漏场景、如何监控及分析详解

目录 前言 哪些情况会引起内存泄漏 1. 意外的全局变量 2. 遗忘的定时器 3. 使用不当的闭包 4. 遗漏的 DOM 元素 5. 网络回调 如何监控内存泄漏 如何分析内存泄漏,找出有问题的代码 实例分析 总结 前言 Q:什么是内存泄漏? 字面上的意思,申请的内存没有及时回收掉,被泄漏了 Q:为什么会发生内存泄漏? 虽然前端有垃圾回收机制,但当某块无用的内存,却无法被垃圾回收机制认为是垃圾时,也就发生内存泄漏了 而垃圾回收机制通常是使用标志清除策略,简单说,也就是引用从根节点开始是否可达来判定

一文搞懂如何避免JavaScript内存泄漏

目录 一.什么是内存泄漏 二.常见的内存泄漏 1.意外的全局变量 2. 计时器 3. 闭包 4. 事件监听器 5.缓存 6.分离的DOM元素 三.识别内存泄漏 1.使用性能分析器可视化内存消耗 2. 识别分离的 DOM 节点 大家好,我是CUGGZ.SPA(单页应用程序)的兴起,促使我们更加关注与内存相关的 JavaScript 编码实践.如果应用使用的内存越来越多,就会严重影响性能,甚至导致浏览器的崩溃.下面就来看看JavaScript中常见的内存泄漏以及如何避免内存泄漏. 一.什么是内存泄漏

一次NodeJS内存泄漏排查的实战记录

目录 前言 案例一 故障现象 排查过程 案例二 故障现象 排查过程 问题原因 node-v9.x 以下的版本 node-v10.x 以上的版本 修复泄露 总结 前言 性能问题(内存.CPU 飙升导致服务重启.异常)排查一直是 Node.js 服务端开发的难点,去年在经过调研后,在我们项目的 Node.js 服务上都接入了 Easy-Monitor 来帮助排查生产环境遇到的性能问题.前段时间遇到了两例内存泄漏的案例,在这里做一个排查经过的整理. 案例一 故障现象 线上的某个服务发生了重启,经过观察

JS前端性能优化解决内存泄漏页面崩溃

目录 发生什么事了? 咋了?这是咋了? 死去的页面突然攻击我? 陷入僵局 垂死病中惊坐起 勿以善小而不为 修改参考 发生什么事了? 一个与往常一样的上午,当我沉浸在业务需求中不可自拔时,突然被拉入到一个事故大群.一脸懵逼的我还以为和之前的每次线上bug一样仅仅是个小问题时,殊不知是我简单了... 看着群里的聊天记录,瞬间一种不好的预感涌上心头.不会是哪个页面写了死循环了吧? 咋了?这是咋了? 死去的页面突然攻击我? 因为项目本身过于庞大,且用户反馈不特定页面崩溃,这使得问题定位难度较大. 经过团

手把手教你如何排查Javascript内存泄漏

目录 引言 如何判断我的应用发生了内存泄漏 Performance和Memory都可以用来定位内存问题,先用谁呢 通过Memory面板定位内存泄漏的流程通常是怎么样的呢 为什么我的内存快照记录下来之后看不懂,还出现了很多奇怪的变量 快照里有一些“Detached DOM tree”,是什么意思 Shallow size 和 Retained size,它们有什么不同 Memory里的Summary视图, Comparison视图, Dominators视图和Containment视图分别有什么不

详谈JavaScript内存泄漏

1.什么是闭包.以及闭包所涉及的作用域链这里就不说了. 2.JavaScript垃圾回收机制 JavaScript不需要手动地释放内存,它使用一种自动垃圾回收机制(garbage collection).当一个对象无用的时候,即程序中无变量引用这个对象时,就会从内存中释放掉这个变量. 复制代码 代码如下: var s = [ 1, 2 ,3];     var s = null;     //这样原始的数组[1 ,2 ,3]就会被释放掉了. 3.循环引用 三个对象 A .B .C AàBàC :

JavaScript内存泄漏的处理方式

下面就是小编整理的关于JS遇到内存泄漏问题时应该采取的处理方式. 随着现在的编程语言功能越来越成熟.复杂,内存管理也容易被大家忽略.本文将会讨论JavaScript中的内存泄漏以及如何处理,方便大家在使用JavaScript编码时,更好的应对内存泄漏带来的问题. 概述 像C语言这样的编程语言,具有简单的内存管理功能函数,例如malloc( )和free( ).开发人员可以使用这些功能函数来显式地分配和释放系统的内存. 当创建对象和字符串等时,JavaScript就会分配内存,并在不再使用时自动释

插件:检测javascript的内存泄漏

转自:http://www.ajaxjs.com/yuicn/bbs/ShowPost.asp?ThreadID=6 2006-10-18 @ 07:59:29 · 作者 volcano Javascript的内存泄漏,不是太可怕.它只会悄悄的,慢慢的把你的浏览器拖的巨慢无比,让你愤怒的拍案而起,大骂微软出品的破烂浏览器危害社会.这一切有可能并不是浏览器的错,可能只是因为网页上有些javascript的内存泄漏罢了. 在科技日益发达今天,我们有必要武装自己,以及自己的浏览器,这样万一浏览器倒下了

nodeJs内存泄漏问题详解

之前一次偶然机会发现,react 在server渲染时,当NODE_ENV != production时,会导致内存泄漏.具体issues: https://github.com/facebook/react/issues/7406 .随着node,react同构等技术地广泛运用,node端内存泄漏等问题应该引起我们的重视.为什么node容易出现内存泄漏以及出现之后应该如何排查,下面通过一个简单的介绍以及例子来说明. 首先,node是基于v8引擎基础上,其内存管理方式与v8一致.下面简单介绍v8

Android 5.1 WebView内存泄漏问题及快速解决方法

问题背景 在排查项目内存泄漏过程中发现了一些由WebView引起的内存泄漏,经过测试发现该部分泄漏只会出现在android 5.1及以上的机型.虽然项目使用WebView的场景并不多,但秉承着一个泄漏都不放过的精神,我们肯定要把它给解决了. 遇到的问题 项目中使用WebView的页面主要在FAQ页面,问题也出现在多次进入退出时,发现内存占用大,GC频繁.使用LeakCanary观察发现有两个内存泄漏很频繁: 我们分析一下这两个泄漏: 从图一我们可以发现是WebView的ContentViewCo

防止动态加载JavaScript引起的内存泄漏问题

为了释放脚本资源,通常在返回后还要一些进行额外的处理. 复制代码 代码如下: script = document.createElement('script'); script.src = 'http://example.com/cgi-bin/jsonp?q=What+is+the+meaning+of+life%3F'; script.id = 'JSONP'; script.type = 'text/javascript'; script.charset = 'utf-8'; // 标签加

javascript removeChild 导致的内存泄漏

为得求证,自己写了一个页面来验证怎样内存泄漏.代码如下 复制代码 代码如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head

Android内存泄漏排查利器LeakCanary

本文为大家分享了Android内存泄漏排查利器,供大家参考,具体内容如下 开源地址:https://github.com/square/leakcanary 在 build.gralde 里加上依赖, 然后sync 一下, 添加内容如下 dependencies { .... debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5' releaseCompile 'com.squareup.leakcanary:leakcanar

深入理解JavaScript程序中内存泄漏

垃圾回收解放了我们,它让我们可将精力集中在应用程序逻辑(而不是内存管理)上.但是,垃圾收集并不神奇.了解它的工作原理,以及如何使它保留本应在很久以前释放的内存,就可以实现更快更可靠的应用程序.在本文中,学习一种定位 JavaScript 应用程序中内存泄漏的系统方法.几种常见的泄漏模式,以及解决这些泄漏的适当方法. 一.简介 当处理 JavaScript 这样的脚本语言时,很容易忘记每个对象.类.字符串.数字和方法都需要分配和保留内存.语言和运行时的垃圾回收器隐藏了内存分配和释放的具体细节. 许

javascript垃圾收集机制与内存泄漏详细解析

javascript具有自动垃圾收集机制,也就是说,执行环境会负责管理代码执行过程中的使用的内存.而在C和C++之类的语言中,开发人员的一项基本任务就是手动跟踪内存的使用情况,这是造成许多问题的一个根源.在编写javascript程序时候,开发人员不用再关心内存使用的问题,所需内存的分配 以及无用的回收完全实现了自动管理.这种垃圾收集机制的原理其实很简单:找出那些不再继续使用的变量,然后释放其中占用的内存.为此,垃圾收集器会按照固定的时间间隔(或代码执行中预设的收集时间),周期性的执行这一操作.