有一个实时应用,使用 nodejs 编写,会每隔一段时间调用远程 grpc ,大概每秒 1 、2 次这样的调用。上线一个月后发现服务器内存占用越来越大。大概占用了 14GB 的内存吧。用 iotop 发现 node 内存炸了。
使用了 alinode dump heap 了,发现了有一个 promisifyCall 占用了大量内存,疑似泄露。
调用链是
自身大小(字节) 总大小(字节) 函数
0 524600 processTicksAndRejections internal/process/task_queues.js
0 524600 updateStatus 我自己的文件
0 524600 publish 这里调用了 grpc 导出的函数
524600 524600 promisifyCall 这里应该就是泄露的函数了
promisifyCall 来自于 https://github.com/bojand/promisify-call ,看了下是被 grpcCaller 引用的 https://github.com/bojand/grpc-caller 。项目中使用了 grpcCaller 去调用 grpc 方法。
const res = await grpcCallerInstance.publish(req);
接着这个 publish 操作就走到promisifyCall
中去了
promisifyCall 的定义看了下,https://github.com/bojand/promisify-call/blob/master/index.js
const wc = require('with-callback')
/**
* Promisifies the call to <code>fn</code> if appropriate given the arguments.
* Calls the function <code>fn</code> either using callback style if last argument is a function.
* If last argument is not a function, <code>fn</code> is called returning a promise.
* This lets you create API that can be called in either fashions.
* @param {Object} ctx context / this
* @param {Function} fn The function to call
* @param {arguments} args Arguments
* @return {undefined|*|Promise} Promise if promisified
*/
function promisifyCall (ctx, fn) {
const args = []
args.push.apply(args, arguments)
args.splice(0, 2)
// check if last (callback) argument is being pased in explicitly
// as it might be undefined or null, in which case we'll replace it
const same = fn.length && args.length === fn.length
const lastIndex = same ? fn.length - 1 : args.length - 1
const lastArg = args && args.length > 0 ? args[lastIndex] : null
const cb = typeof lastArg === 'function' ? lastArg : null
if (cb) {
return fn.apply(ctx, args)
}
return wc(callback => {
same
? args[lastIndex] = callback
: args.push(callback)
fn.apply(ctx, args)
})
}
隐约感觉里面的 args 变量可能会导致泄露。但还是没想明白怎样才会发生这个泄露。
1
msmmbl OP 我可能搞错了,524600 字节并不大。再抓一晚上数据看看。
|
2
zhzbql 2021-11-22 18:07:24 +08:00
Node.js 不用 buffer 不是最大内存 1.4GB 吗,兄弟你这 14GB 怎么搞出来的
|