背景

在阿里云上看到我运行了一段时间的程序,发现 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 里的定时脚本

Node 内存泄漏排查案例的更多相关文章

  1. 轻松排查线上Node内存泄漏问题

    I. 三种比较典型的内存泄漏 一. 闭包引用导致的泄漏 这段代码已经在很多讲解内存泄漏的地方引用了,非常经典,所以拿出来作为第一个例子,以下是泄漏代码: 'use strict'; const exp ...

  2. Chrome JS内存泄漏排查方法(Chrome Profiles)

     原文网址:http://blog.csdn.net/kaitiren/article/details/19974269 JS内存泄漏排查方法(Chrome Profiles)   Google Ch ...

  3. windows 下面的内存泄漏排查.

    内存泄漏排查 一下本人只是简单的介绍一个实用, 如果读者很感兴趣, 可以查阅msdn自己去深入调查相关的API和原理. API 介绍 1. 马上打印泄漏信息:_CrtDumpMemoryLeaks() ...

  4. Java内存泄漏真实案例

    内存泄漏:当不再需要一个对象时,垃圾收集器会回收它:如果不需要的对象一直在产生而不被收回,就称作“内存泄漏”. 以下为本人在工作中遇到的内存泄漏的案例: 1.对于大量的请求,使用了Executors. ...

  5. 填坑总结:python内存泄漏排查小技巧

    摘要:最近服务遇到了内存泄漏问题,运维同学紧急呼叫解决,于是在解决问题之余也系统记录了下内存泄漏问题的常见解决思路. 本文分享自华为云社区<python内存泄漏排查小技巧>,作者:luti ...

  6. Netty堆外内存泄漏排查,这一篇全讲清楚了

    上篇文章介绍了Netty内存模型原理,由于Netty在使用不当会导致堆外内存泄漏,网上关于这方面的资料比较少,所以写下这篇文章,专门介绍排查Netty堆外内存相关的知识点,诊断工具,以及排查思路提供参 ...

  7. JS内存泄漏排查方法——Chrome Profiles

    一.概述 Google Chrome浏览器提供了非常强大的JS调试工具,Heap Profiling便是其中一个.Heap Profiling可以记录当前的堆内存(heap)快照,并生成对象的描述文件 ...

  8. 内存泄漏学习案例-1-ArrayList

    解决 内存泄漏 于是赶快登陆探测服务器,首先是 top free df 三连,结果还真发现了些异常. 我们的探测进程 CPU 占用率特别高,达到了 900%. 我们的 Java 进程,并不做大量 CP ...

  9. Spring Boot引起的“堆外内存泄漏”排查及经验总结

    小结: 检索词:C++内存分配器.jvm内存模型.gdb.内存泄露 https://tech.meituan.com/2019/01/03/spring-boot-native-memory-leak ...

随机推荐

  1. Java相同id的数据集合,合并数据为一条,并将几个字段内容合并为一个

    Java实现,当然也可以数据库实现; /** * Created by shaozhiqi on 2019/7/31. */ public class TestUnion { @Test public ...

  2. pgsql中的lateral使用小结

    pgsql中的lateral 什么是LATERAL 带有LATERAL的SQL的计算步骤 LATERAL在OUTER JOIN中的使用限制(或定义限制) LATERAL的几个简单的例子 总结 举几个我 ...

  3. unity3d的键盘和鼠标输入

    一.键盘的输入 •GetKey,GetKeyDown,GetKeyUp三个方法分别获取用户键盘按键的输入 1. GetKey:用户长按按键有效: bool down = Input.GetKeyDow ...

  4. cool-yogurt小组采访感想

    “对于这个小组项目的选题,其实最初的那个版本我还是被“感动”到的,因为我自己以前确实有这样的类似体验和需求,以前非常喜欢一个球星,因此想知道关于他所有的事情,想知道他每一场比赛的数据,新闻有哪些报道, ...

  5. C语言二维数组超细讲解

    用一维数组处理二维表格,实际是可行的,但是会很复杂,特别是遇到二维表格的输入.处理和输出. 在你绞尽脑汁的时候,二维数组(一维数组的大哥)像电视剧里救美的英雄一样显现在你的面前,初识数组的朋友们还等什 ...

  6. net core天马行空系列:降低net core门槛,数据库操作和http访问仅需写接口,实现类由框架动态生成

    引文   hi,大家好,我是三合.不知各位有没有想过,如果能把数据库操作和http访问都统一封装成接口(interface)的形式, 然后接口对应的实现类由框架去自动生成,那么必然能大大降低工作量,因 ...

  7. 如何将Python项目发布到PyPI

    The Python Package Index (PyPI) is a repository of software for the Python programming language. 如何打 ...

  8. python 携程asyncio实现高并发示例1

    import asyncio #携程(携程不是函数) async def print_hello(): while True: print("hello world") await ...

  9. 干货!Python中字符串、列表、元祖、字典,集合之间的转换

    一.字符串的转化 1.字符串转换成列表 字符串转换成list 的时候,str可以作为迭代对象,直接放入:也可以使用split对字符串进行切割.然后返回list s = '1a1b1c' print(l ...

  10. java中String StringBuilder StringBuffer比较和效率(性能)测试

    string stringbuilder stringbuffer三者的区别 从JDK源码看,String.StringBuilder.StringBuffer都是存放在char[] 数组字符串. 简 ...