您当前的位置: 首页 >> 热点 > >> 正文

Node.js HTTP 模块的内存泄露问题-世界视讯

来源:编程杂技 2023-07-03 10:04:39发布

很久没有逛社区了,晚上回来看了一下最近的情况,突然看到一个内存泄露问题,作为一个 APM 开发者,自然想分析其中的原因。


(资料图片)

问题

下面介绍一下具体的问题。看一下 demo。

const http = require("http")async function main () {  let i = 0  while (true) {    if (i % 100 === 0) {      global.gc()    }    if (i % 10000 === 0) {      console.log(process.memoryUsage().heapUsed)    }    http.createServer((req, res) => {})    i++  }}main()

Node.js v20.3.1 下执行上面代码(node --expose-gc demo.js)输出如下。

268112011409488196327922803801636438104

可以看到内存不断在增长。下面来分析这个问题。

分析
const http = require("http");const v8 = require("v8");for (i = 0; i < 1000; i++) {    http.createServer((req, res) => {});}v8.writeHeapSnapshot("memory-leaky.heapsnapshot");

采集的快照如下。

图片

可以看到,Server 对象没有被释放。看一下是谁引用了它。

图片

是定时器引用了 Server 对象,我们看一下定时器对象又是被谁引用了。

图片

有一个关键的变量 connectionsCheckingInterval,到 Node.js 源码里看一下,最终发现是 Server 初始化时创建的。

function Server(options, requestListener) {  setupConnectionsTracking(this);}function setupConnectionsTracking(server) {  server[kConnectionsCheckingInterval] = setInterval(checkConnections.bind(server), server.connectionsCheckingInterval).unref();}

可以看到 checkConnections.bind 返回的匿名函数持有了 Server,而匿名函数又被 setInterval 持有了,所以导致 Server 对象无法释放。

修复

那么如何修复这个问题呢?修复这个问题,首先需要了解 setupConnectionsTracking 是做什么的,逻辑如下。

function checkConnections() {  if (this.headersTimeout === 0 && this.requestTimeout === 0) {    return;  }  const expired = this[kConnections].expired(this.headersTimeout, this.requestTimeout);  for (let i = 0; i < expired.length; i++) {    const socket = expired[i].socket;    if (socket) {      onRequestTimeout(socket);    }  }}

可以看到,setupConnectionsTracking 是追踪连接超时,回到我们的测试例子中可以发现,我们并没有执行 listen,也就是说,Server 对象并不会处理连接,那么也就没有连接需要追踪,所以修复方式就是把调用 setupConnectionsTracking 的时机延迟到 listen 成功时,修复代码大致如下。

function Server(options, requestListener) {  this.on("listening", () => {    setupConnectionsTracking(this);  });}

修改源码重新编译后测试结果如下。

36535524002680375340037629763773088

可以看到内存已经不会增长了,采集快照也可以看到不会再存在大量 Server 对象。

总结

这个例子虽然看起来有点不常见,用法也很怪异,但是从侧面说明了虽然 JS 自带 GC,但是因为逻辑 / 引用关系复杂,还是很容易出现内存泄露问题,所以写代码时还是需要注意,具体的 issue 可以参考 https://github.com/nodejs/node/issues/48604。

上一篇 下一篇
x
推荐阅读 更多

Node.js HTTP 模块的内存泄露问题-世界视讯

编程杂技 2023-07-03

腾龙健康IPO:境外销售占比超八成藏风险 账面资金充足却募资补流

中国网证券 2023-07-03

曲麻莱牦牛产业再上台阶 签订681万元购销合同

西海都市报 2023-07-03

恭喜!林高远夺男单冠军|全球观焦点

荆楚网 2023-07-03

天职师大“师生齐接力” 为建设文明津城文明校园作贡献 全球快播

天津日报 2023-07-03

百强房企上半年销售总额超3.5万亿元 下半年市场将如何走?

证券日报 2023-07-03

加密文件怎么打印_加密文件|滚动

互联网 2023-07-03

灯头规格型号_灯头型号

互联网 2023-07-03

暗香·兰(关于暗香·兰的简介)

互联网 2023-07-03

天天微资讯!《七月与安生》结局_七月与安生电影的结局是什么意思

互联网 2023-07-03