很久没有逛社区了,露问晚上回来看了一下最近的模块情况,突然看到一个内存泄露问题,存泄作为一个 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。
责任编辑:武晓燕 来源: 编程杂技 HTTP模块内存(责任编辑:热点)
银保监会:前10个月房地产合理贷款需求得到满足 信贷结构持续优化
ST地矿(000409.SZ):拟向关联方兖矿集团借款不超12亿元 构成关联交易
泸天化(000912.SZ)总市值95.5亿元 农行四川分行拟减持不超2%
汤姆猫(300459.SZ)股东股份转让计划届满暨预披露公告
四川阿坝州提高孤儿基本生活最低养育标准 2022年1月起执行