排查 Node.js 服务内存泄漏,没想到竟是它?

今日文章由作者 @林乐扬 投稿分享。

背景

团队最近将两个项目迁移至 degg 2.0 中,两个项目均出现比较严重的内存泄漏问题,此处以本人维护的埋点服务为例进行排查。服务上线后内存增长如下图,其中红框为 degg 2.0 线上运行的时间窗口,在短短 36 小时内,内存已经增长到 50%,而平时内存稳定在 20%-30%,可知十之八九出现了内存泄漏。

排查思路

由于两个接入 degg 2.0 的服务均出现内存泄漏问题,因此初步将排查范围锁定在 degg 2.0引入或重写的基础组件上,重点怀疑对象为 nodex-logger 组件;同时为了排查内存泄漏,我们需要获取服务运行进程的堆快照(heapsnapshot),获取方式可参看文章 hyj1991:Node 案发现场揭秘 —— 快速定位线上内存泄漏https://zhuanlan.zhihu.com/p/36340263 。

排查过程

一、获取堆快照

使用 alinode 获取堆快照,服务启动后,使用小流量预热一两分钟便记录第1份堆快照(2020-4-16-16:52),接着设置 qps 为 125 对服务进行施压,经过大约一个小时(2020-4-16-15:46)获取第2份堆快照。使用 Chrome dev工具载入两份堆快照,如下图所示,发现服务仅短短运行一小时,其堆快照文件就增大了 45MB,而初始大小也不过 39.7MB;我们按 Retained Size 列进行排序,很快就发现了一个『嫌疑犯』,即 generator;该项占用了 55% 的大小,同时 Shallow Size 却为 0%,一项一项地展开,锁定到了图中高亮的这行,但是继续展开却提示 0%,线索突然断了。

盯着 generator 进入思考,我的服务代码并没有generator 语法,为什么会出现 generator 对象的内存泄漏呢?此时我把注意力转到 node_modules 目录中,由于最近一直在优化 nodex-kafka 组件,有时直接在 node_modules 目录中修改该组件的代码进行调试,因此几乎每个文件头部都有的一段代码引起了我的注意:

"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};

这个代码是 typescript 源码编译后的产出,由于代码使用了 async/await 语法,因此都编译成 __awaiter 的形式,在源码中使用 async 函数的地方,在编译后都使用 __awaiter 进行包裹:

// 编译前
(async function() {
  await Promise.resolve(1);
  await Promise.resolve(2);
})()

// 编译后
(function () {
  return __awaiter(this, void 0, void 0, function* () {
    yield Promise.resolve(1);
    yield Promise.resolve(2);
  });
})();

同时一个关于 generator 内存泄漏的 #3075

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值