zoukankan      html  css  js  c++  java
  • Node 内存泄漏排查案例

    背景

    在阿里云上看到我运行了一段时间的程序,发现 memory 一项基本是在稳步提升,就知道有内存泄漏的情况出现。如下图

    近三日从 35% 升到 40%,缓慢而坚定的提升。

    代码

    排查此问题需要分析其堆内存快照,当然我们不能直接使用线上机器调试。不幸的是测服机器在内网,和阿里云联不通,alinode 发挥不了作用。但所幸的是 V8 引擎提供了内部接口可以直接把堆中的JS对象导出来供开发者分析。我们采用heapdump这个模块,执行如下命令安装

    $ npm install heapdump --save
    
    "heapdump": "^0.3.15",
    

    执行如下

    const heapdump = require('heapdump');
    heapdump.writeSnapshot(`./${Date.now()}.heapsnapshot`);
    

    生成的文件如下

    $ ll -lh
    
    -rw-rw-r-- 1 souche souche  38M Nov 19 19:00 1574161221512.heapsnapshot
    

    总之我在测服上定时每 2 小时打印堆栈快照。

    总之,你可以使用 scp 命令把测服的代码导出到本地

    # 传递单个文件
    
    $ scp 【服务器用户名】@【服务器地址】:【服务器上存放文件的路径】【本地文件的路径】
    
    # 例如
    
    $ scp souche@172.11.xxx.xxx:/home/souche/app/egg-test/current/1574161221512.heapsnapshot /Users/dasouche/workspace/sc-node
    
    # 传递文件夹
    
    scp -r 【服务器用户名】@【服务器地址】:【服务器上存放文件的路径】【本地文件的路径】
    

    分析步骤

    打开 chrome-控制台-Memory-load

    加载完后得到

    简而言之,Shallow Size 就是对象自身被创建时所需要内存的大小,Retained Size 就是当把对象从支配树上拿掉,对象和它的下级节点一共能释放的内存大小。

    其术语简介可参见:https://developers.google.com/web/tools/chrome-devtools/memory-problems/memory-101

    分析过程

    从线上机器导出两个堆文件,一个是10月30日打印的,一个是11月4日打印的,其内存上升了 100+ MB。

    比对两个堆,把第二个堆文件的 Summary 切换成 Comparison,并按 Delta 倒叙排,发现增长最快的是 (concatenated string) 。其中有很多连接字符串,其中有大量的sql语句,并且有大量的schedule执行。

    (constructor) 增长排第二,其中也见到不少 schedule,那我们可以确认就是 noticeJob.ts 这个定时器的问题。

    本项目使用了 egg 作为框架,schedule 就是指定时触发的逻辑。联系代码我们发现在一个 5 秒触发一次的 schedule 里,里面不停的触发队列的 process 监听事件,猜测是 Queue.process 监听事件越绑越多的毛病,也导致里面的逻辑越触发越多。

    这其实就是队列绑定监听事件的误用了。

    // app/schedule/noticeJob.ts
    'use strict';
    
    import { Context } from 'egg';
    import * as kue from 'kue';
    
    module.exports = {
      schedule: {
        disable: false,
        // 每五秒触发一次
        cron: '*/5 * * * * *',
        immediate: true,
        type: 'worker',
      },
    
      async task(ctx: Context) {
        const Queue = ctx.app.kue;
    
        Queue.process('noticeCalling', async function(job, done) {
          const { uid, rid, subId } = job.data;
          await ctx.service.message.noticedCalling(uid, rid);
          // done();
        });
      },
    };
    
    

    我们在测服注释掉这段定时器后,每隔一小时打印一次(因为测服无法连阿里云),观察一天,内存没有上升趋势,这很好。

    -rw-rw-r--  1 souche souche  38M Nov 24 11:24 1574565877609.heapsnapshot
    -rw-rw-r--  1 souche souche  37M Nov 24 12:24 1574569477611.heapsnapshot
    -rw-rw-r--  1 souche souche  38M Nov 24 13:24 1574573077611.heapsnapshot
    -rw-rw-r--  1 souche souche  38M Nov 24 14:24 1574576677613.heapsnapshot
    -rw-rw-r--  1 souche souche  38M Nov 24 15:24 1574580277614.heapsnapshot
    -rw-rw-r--  1 souche souche  38M Nov 24 16:24 1574583877614.heapsnapshot
    -rw-rw-r--  1 souche souche  38M Nov 24 17:24 1574587477616.heapsnapshot
    -rw-rw-r--  1 souche souche  38M Nov 24 18:24 1574591077616.heapsnapshot
    -rw-rw-r--  1 souche souche  38M Nov 24 19:24 1574594677616.heapsnapshot
    -rw-rw-r--  1 souche souche  38M Nov 24 20:24 1574598277618.heapsnapshot
    -rw-rw-r--  1 souche souche  37M Nov 24 21:24 1574601877620.heapsnapshot
    -rw-rw-r--  1 souche souche  38M Nov 24 22:24 1574605477621.heapsnapshot
    -rw-rw-r--  1 souche souche  38M Nov 24 23:24 1574609077622.heapsnapshot
    -rw-rw-r--  1 souche souche  38M Nov 25 00:24 1574612677622.heapsnapshot
    -rw-rw-r--  1 souche souche  38M Nov 25 01:24 1574616277622.heapsnapshot
    -rw-rw-r--  1 souche souche  38M Nov 25 02:24 1574619877623.heapsnapshot
    -rw-rw-r--  1 souche souche  38M Nov 25 03:24 1574623477624.heapsnapshot
    -rw-rw-r--  1 souche souche  38M Nov 25 04:24 1574627077626.heapsnapshot
    -rw-rw-r--  1 souche souche  38M Nov 25 05:24 1574630677627.heapsnapshot
    -rw-rw-r--  1 souche souche  38M Nov 25 06:24 1574634277627.heapsnapshot
    -rw-rw-r--  1 souche souche  38M Nov 25 07:24 1574637877628.heapsnapshot
    -rw-rw-r--  1 souche souche  38M Nov 25 08:24 1574641477629.heapsnapshot
    -rw-rw-r--  1 souche souche  38M Nov 25 09:24 1574645077630.heapsnapshot
    -rw-rw-r--  1 souche souche  39M Nov 25 10:24 1574648677630.heapsnapshot
    -rw-rw-r--  1 souche souche  39M Nov 25 11:24 1574652277632.heapsnapshot
    

    解决方法

    最后就在 app.ts 设置这个 process 的监听,移除 schedule 里的定时脚本

  • 相关阅读:
    Object.prototype.toString.call()进行类型判断
    JavaScript中的typeof操作符用法实例
    js ==与===区别(两个等号与三个等号)
    js nextSibling属性和previousSibling属性概述及使用注意
    Java 缓存技术之 ehcache
    不可不知 DDoS的攻击原理与防御方法
    jQuery的选择器中的通配符[id^='code']
    jquery $("[id$='d']").val();这句话什么意思?
    js 数组的操作
    【转】理解js中的原型链,prototype与__proto__的关系
  • 原文地址:https://www.cnblogs.com/everlose/p/12846250.html
Copyright © 2011-2022 走看看