4

如何定位内存泄露

 2 years ago
source link: https://segmentfault.com/a/1190000040887757
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

完整高频题库仓库地址:https://github.com/hzfe/awesome-interview

完整高频题库阅读地址:https://febook.hzfe.org/

  • 垃圾回收机制

回答关键点

垃圾回收 DevTools

内存泄漏是指不再使用的内存,没有被垃圾回收机制回收。当内存泄漏很大或足够频繁时,用户会有所感知:轻则影响应用性能,表现为迟缓卡顿;重则导致应用奔溃,表现为无法正常使用。为了避免内存泄漏带来的不良影响,需要对垃圾回收机制进行了解,掌握内存泄漏分析方法,完善线上相关监控措施。

内存泄漏定位和分析一般需要辅助工具,比如 Chrome DevTools。开发者可以通过 DevTools 记录页面活动概况,生成可视化分析结果,从时间轴中直观了解内存泄漏情况;利用 DevTools 获取若干次内存快照,检查内存堆栈变化;以及使用 Chrome 任务管理器,实时监控内存的使用情况。

知识点深入

1. 排查内存泄漏常见问题

在 JavaScript 中,当一些不再需要的数据仍然可达时,V8 会认为这些数据仍在被使用,不会释放内存。为了调试内存泄漏,我们需要找到被错误保留的数据,并确保 V8 能够将其清理掉。

代码量较小时,开发者通常可以基于以下基本原则进行快速自查:

  1. 是否滥用全局变量,没有手动回收。
  2. 是否没有正确销毁定时器、闭包。
  3. 是否没有正确监听事件和销毁事件。

除此之外,开发者可以借助外部工具进行内存泄漏排查。

2. 使用 Chrome DevTools 定位内存泄漏

Performance

image

打开准备分析的页面和 DevTools 的 Performance 面板,勾选 Memory 并开始录制,在模拟用户操作一段时间后结束录制,DevTools 会将这段时间内的页面行为活动进行记录和分析。

通过生成的结果可以直观查看到内存时间线,了解内存随时间的占用变化,如果内存占用曲线成阶梯状一直上升,则可能存在内存泄漏。按需选取时间线中的区域片段,检查对应时间段内的活动类型和时间占用,作为排查和定位内存泄漏的辅助办法。

Memory

image

打开准备分析的页面和 DevTools 的 Memory 面板,按需生成快照。每个快照的内容是快照时刻,进行一次垃圾回收后,应用中所有可达的对象。

当开发者明确知道与内存泄漏关联的用户交互步骤时,可以生成多次内存快照进行对比,排查出泄漏的对象:在做用户交互操作之前,进行一次正常内存堆栈信息的快照;在做用户交互操作中或操作结束时,进行内存快照。使用 Comparison 视图或使用 filter 按需查看快照之间的差异。

上面的图中使用 filter 检查快照 2 和快照 3 的差异,通过结果可知在两个快照之间持续被分配 clickCallback 闭包。通过点击文件路径可以定位到内存泄漏的代码。

image

3. Node.js 中的内存泄漏定位

如果需要定位 Node.js 中的内存泄漏,启动 Node.js 时带上 --inspect 参数,以便利用 Chrome DevTools 工具生成 Memory 快照数据。如图所示,启动 Node.js 服务后,打开 Chrome DevTools,会有 Node 标识,点击可以打开 Node 专用 DevTools。

image

除此之外,也可以借助第三方包 heapdump 生成快照文件,导入至 Chrome DevTools 中的 Memory 进行快照对比。

启动 Node.js 时带上 --expose-gc 参数以便调用 global.gc() 方法触发垃圾回收。借助 process.memoryUsage().heapUsed 检查内存大小,作为内存泄漏的辅助判断。

const heapdump = require("heapdump");

const capture = function () {
  global.gc();
  heapdump.writeSnapshot("./HZFE_HEAPSNAPSHOT/" + Date.now() + ".heapsnapshot");
  console.log("heapUsed:", process.memoryUsage().heapUsed);
};

capture();

/* 可能有内存泄漏的代码片段 start */
// code
/* 可能有内存泄漏的代码片段 end */

capture();

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK