缓存爆炸通过 Object/Map 的内存缓存可以极大地提升程序性能 , 但是很有可能未控制好缓存的大小和过期时间 , 导致失效的数据仍缓存在内存中 , 导致内存泄漏:
const cache = {};function setCache() { cache[Date.now()] = new Array(1000);}setInterval(setCache, 100);
上面这段代码中 , 会不断的设置缓存 , 但是没有释放缓存的代码 , 导致内存最终被撑爆 。
如果的确需要进行内存缓存的话 , 强烈建议使用 lru-cache 这个 npm 包 , 可以设置缓存有效期和最大的缓存空间 , 通过 LRU 淘汰算法来避免缓存爆炸 。
内存泄漏定位实操当出现内存泄漏的时候 , 定位起来往往十分麻烦 , 主要有两个原因:
- 程序开始运行的时候 , 问题不会立即暴露 , 需要持续的运行一段时间 , 甚至一两天 , 才会复现问题 。
- 出错的提示信息非常模糊 , 往往只能看到 heap out of memory 错误信息 。
接下来通过上文中闭包引用里内存泄漏的例子 , 来实际操作一把 。首先 npm install heapdump 安装后 , 修改代码为下面的样子:
// 一段存在内存泄漏问题的示例代码const heapdump = require('heapdump');heapdump.writeSnapshot('init.heapsnapshot'); // 记录初始内存的堆快照let i = 0; // 记录调用次数let theThing = null;let replaceThing = function() {const newThing = theThing;let unused = function() {if (newThing) console.log("hi");};// 不断修改引用theThing = {longStr: new Array(1e8).join("*"),someMethod: function() {console.log("a");},};if (++i >= 1000) {heapdump.writeSnapshot('leak.heapsnapshot'); // 记录运行一段时间后内存的堆快照process.exit(0);}};setInterval(replaceThing, 100);
【全局变量、事件绑定、缓存爆炸?Node.js内存泄漏问题分析】在第 3 行和第 22 行 , 分别导出了初始状态的快照和循环了 1000 次后的快照 , 保存为 init.heapsnapshot 与 leak.heapsnapshot 。然后打开 Chrome 浏览器 , 按下 F12 调出 DevTools 面板 , 点击 Memory 的 Tab , 最后通过 Load 按钮将刚刚的两个快照依次导入:
文章插图
mark
导入后 , 在左侧可以看到堆内存有明显的上涨 , 从 1.7 MB 上涨到了 3.1 MB , 几乎翻了一倍:
文章插图
接下来就是最关键的步骤了 , 点击 leak 快照 , 然后将其与 init 快照进行对比:
文章插图
右侧红框圈出来了两列:
- Delta:表示变化的数量
- Size Delta:表述变化的空间大小
文章插图
文章插图
从这两个图中 , 可以很直观的看出来主要是 theThing.someMethod 这个函数的闭包上下文和 theThing.longStr 这个很长的拼接字符串造成的内存泄漏 , 到这里问题就基本定位清楚了 , 我们还可以点击下方的 Object 模块来更清楚的看一下调用链的关系:
文章插图
图中很明显的看出来 , 内存泄漏原因就是因为 newTHing <- 闭包上下文 <- someMethod<- 上一次 newThing 这样的链式依赖关系导致内存的快速增长 。图中第二列的 distance 表示的是该变量距离根节点的距离 , 因而最上级的 newThing 是最远的 , 表示的是下级引用上级的关系 。
参考文章
- Visualizing memory management in V8 Engine
推荐阅读
- 曹真厉害还是曹休厉害
- Excel合并单元格的常见操作,序号填充、求和一步搞定,这些都是干货
- CSS3绘制各种形状:弧形、心形、星星、箭头,通通不在话下
- linux编程yum 命令详解
- 维生素b可以减肥吗?
- 马蹄汤的做法
- 胡萝卜土豆排骨汤
- 肉杂拌汤
- 女生唯美的二字昵称有哪些?
- 银元|家里面找出很多1分、2分、5分硬币,收藏价值怎么样呢?