有一个实时应用,使用 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 怎么搞出来的 |