V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
msmmbl
V2EX  ›  Node.js

帮忙分析下这段 nodejs 代码的内存泄漏原因

  •  
  •   msmmbl · 317 天前 · 3323 次点击
    这是一个创建于 317 天前的主题,其中的信息可能已经有所发展或是发生改变。

    有一个实时应用,使用 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 条附言  ·  317 天前
    重启应用后内存不涨了,先再跑一个月看看
    msmmbl
        1
    msmmbl  
    OP
       317 天前
    我可能搞错了,524600 字节并不大。再抓一晚上数据看看。
    zhzbql
        2
    zhzbql  
       317 天前
    Node.js 不用 buffer 不是最大内存 1.4GB 吗,兄弟你这 14GB 怎么搞出来的
    msmmbl
        3
    msmmbl  
    OP
       317 天前
    @zhzbql 同时起了多个进程
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2314 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 38ms · UTC 08:15 · PVG 16:15 · LAX 01:15 · JFK 04:15
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.