likes
comments
collection
share

大家好,我是 fuite:一款针对内存泄漏的性能分析神器

作者站长头像
站长
· 阅读数 24

给前端以福利,给编程以复利。大家好,我是大家的林语冰。

00. 写在前面

调试 Web 应用的内存泄漏难如脱单。虽然此类性能分析工具层出不穷,但大都错综复杂。

最无语子的是,这些工具通常回答不了一个简单的问题:为什么老子的应用程序它喵地会泄漏内存?

因此,我敢打赌,大多数 Web 开发者从不主动监控内存泄漏。可是啊可是,如果你不去测试性能,那么 bug 迟早会像出手的回旋镖,不是不爆,只是时候未到。

本文是性能分析工具 fuite 作者的一篇博客,简单科普了如何利用 fuite 分析内存泄漏。如果你对性能分析或内存监控之类的工具感兴趣,fuite 或许可以提高你的开发幸福感。

大家好,我是 fuite:一款针对内存泄漏的性能分析神器

免责声明 本文属于是语冰的直男翻译了属于是,略有删改,仅供粉丝参考。英文原味版请传送 Introducing fuite: a tool for finding memory leaks in web apps

01. fuite 的诞生背景

当我第一次钻研内存泄漏时,我认为这是一桩罕见的事情。夭寿啦,JavaScript 作为一种带有自动垃圾收集器的语言,怎么可能是内存泄漏的“万恶之源”呢?

但当我学习得越多,我就越怀疑,实际上,内存泄漏在 SPA(单页应用程序)中司空见惯,只是开发者对此视若无睹,就像违章停车一样!

由于大多数 Web 开发者不会为了好玩而摆弄 Chrome 的内存工具,因此它们可能不会注意到内存泄漏。直到有一天,浏览器由于内存不足原地崩溃,或者页面性能慢如龟速,又或者有个“天选之子”碰巧打开了任务管理器,发现某个网站居然占用了兆字节、甚至千兆字节的内存!

但这时候,情况往往已经惨不忍睹,因为同一页面上可能存在多个内存泄漏。这就是 fuite(“泄漏”的法语)的设计动机和用武之地。fuite 是一个 CLI 工具,你可以输入任何 URL,然后它会分析页面是否存在内存泄漏。

举个栗子,测试一下 Vue 官网有没有内存泄漏:

npx fuite https://vuejs.org

默认情况下,fuite 会假设给定站点是客户端渲染的 SPA,并且它会爬取页面,查找各种内部链接。然后,针对每个链接,fuite 会运行以下步骤:

  1. 点击链接
  2. 按按浏览器的后退按钮
  3. 重复查看内存是否增长

一旦 fuite 发现任何内存泄漏,它会显示是哪些怀疑对象导致的。

大家好,我是 fuite:一款针对内存泄漏的性能分析神器

为此,fuite 会启动 Chrome,运行某个场景 n 次(默认为 7 次),并查看是否有任何对象泄漏 n 倍的次数(7、14、21 等)。

fuite 还会分析任何数组、对象、MapSet、事件侦听器和整个 DOM,查看其中是否存在内存泄漏。举个栗子,如果一个数组在迭代 7 次后恰好增长了 7 倍,那么它可能是内存泄漏。

02. 测试现实开发的网站

令人喵瞪狗呆的是,单击内部链接,并按按后退按钮的“基本”场景,就足以在许多 SPA 中发现内存泄漏。

我测试了 10 个人气爆棚的前端框架主页,发现所有框架都有内存泄漏:

站点检测到泄漏内部链接平均增长最大增长
站点 1153.49MB4.28MB
站点 2435.57 MB7.37 MB
站点 31614.9MB186MB
... ...... ...... ...... ...

在这种情况下,“内部链接”是指测试的内部链接数量,“平均增长”是指单击每个链接、然后按后退按钮的平均内存增长,“最大增长”是指泄露最多的内部链接。

粉丝请注意,这些数字不包括一次性的设置成本,因为在默认的 7 次迭代之前,fuite 已经进行了一次预检迭代。

如果想要亲眼验证这些结果,你可以使用 Chrome 开发者工具的内存选项卡。这是我的测试中性能最差的网站的屏幕截图,我在其中单击链接,按后退按钮,拍摄堆快照,然后重复:

大家好,我是 fuite:一款针对内存泄漏的性能分析神器

可以看到,在这个站点上,每次单击链接并返回时,内存都会增加约 6 MB。

为了避免点名批评”贴脸开团“,我没有爆料实际的被测网站的身份。我们的重点只是展示某些人气爆棚的 SPA 示例。

03. 内存泄漏的误区

粉丝请注意,并非 SPA 中的每种内存泄漏都是亟待解决的重大隐患。

举个栗子,SPA 需要维护焦点和滚动状态,从而正确支持可访问性,这意味着,SPA 可能会为每个页面导航存储某些迷你元数据。fuite 会兢兢业业地报告此类内存泄漏,因为它们确实是内存泄漏,但开发者可以按需决定是否需要查缺补漏。

某些内存增长也可能是浏览器的内部更改造成的,而网页无法真正控制这些更改。因此,内存增长数字并不能完美地衡量修复内存泄漏所带来的收益,大概率几 kB 的增长是无法避免的。

fuite 会试图忽略浏览器的内部增长,且当且仅当 Web 开发者提供可行的建议时,fuite 才会报告“检测到内存泄漏”。

在极少数情况下,某些内存增长也可能是浏览器错误造成的。在分析上述网站时,我实际上发现了一个网站似乎由于 <img loading="lazy"> 未卸载而受到该 Chrome 错误的影响。不幸的是,fuite 很难检测到浏览器错误。因此,如果你对内存泄漏感到困惑,最好与其他浏览器进行交叉检查!

另请注意,MAP(多页面应用程序)几乎不可能泄漏,因为假设浏览器没有错误,那么它会清除每个页面导航的内存。

在测试过程中,我发现了两个主页是 MPA 的前端框架,不出所料,fuite 在其中找不到任何内存泄漏。内存泄漏对于 SPA 而言更令人头大,因为每次导航时内存不会自动清除。fuite 主要是为 SPA 设计的,但你也可以在 MPA 上运行它。

目前 fuite 能且仅能测量页面主框架中的 JavaScript 堆内存,无法测量跨域 iframe、Web Workers 和 Service Workers。诸如 performance.measureUserAgentSpecificMemory() 之类的 API 会更精准,但它能且仅能在跨源隔离环境中奏效,因此目前对于通用工具而言并不实用。

04. 其他内存泄漏场景

“爬取内部链接”只是 fuite 的默认场景,你也可以构建自己的场景。

fuite 建立在 Puppeteer 之上,因此对于你想要测试的任何场景,只需要编写一个 Puppeteer 脚本,告诉浏览器要做什么就欧了。

你可能想要测试的某些常见场景是:

  • 打开对话模态框,然后将其关闭
  • 将鼠标悬停在元素上显示工具提示,然后将鼠标移开将其关闭
  • 滚动浏览无限加载列表,然后导航离开和返回
  • ... ...

在每种场景下,你都期望测试前后内存一致。但网络应用程序并不总是那么简单,你可能会惊讶地发现,大多对话框和工具提示都存在内存泄漏。

为了分析内存泄漏,fuite 会捕获堆快照文件,你可以将其加载到 Chrome 开发者工具中检查。它还具有 --debug 模式,你可以使用该模式更细粒度地分析:在运行时逐步执行测试、实时调试浏览器、分析内存泄漏对象等。

高潮总结

在底层,fuite 是一个基本工具,我不会当舔狗,无脑尬吹它可以百分百完成修复内存泄漏的工作。这仍然需要人为因素来弄清楚为什么分配和保留对象,然后找到合理的解决方案。

但我的目标是自动化约 95% 的工作,这样修复 Web 应用程序中的内存泄漏实际上就码到功成。

参考文献

粉丝互动

本期话题是:你是如何搞定内存泄漏的,有没有什么其他性能分析神器推荐?你可以在本文下方自由言论,文明科普。

欢迎持续关注“前端俱乐部”,给前端以福利,给编程以复利。

坚持阅读的小伙伴可以给自己点赞!谢谢大家的点赞,掰掰~

大家好,我是 fuite:一款针对内存泄漏的性能分析神器

转载自:https://juejin.cn/post/7380663251632537615
评论
请登录