nodejs内存泄漏分析-完整步骤

 


前言

一直以来内存泄漏一直是大家比较苦恼不知道如何定位解决的问题,记一次实践,希望可以帮助有类似的问题的小伙伴,欢迎留言讨论,直接上干货

一、泄露问题是什么?

我们的nodejs信令服务器线上运行,但是每当用户量很大或者几天不重启,就会发现内存飙升,之后就会cpu飙高,不确定是否有关,以至于我们必须每天凌晨定时重启服务器释放内存,但是还是有时出现事故,决心彻底解决nodejs内存泄露的问题

 

 

二、内存泄漏怎么定位?

1.easy-monitor

网上看见这个工具直接集成到代码里,就可以通过界面搜集cpu和memery的信息,但是检测memery并没有提供有用的信息,而是提示没问题,所以不太靠谱,检测cpu的简单用了几次,不太确定好不好用,而且使用要非常小心,如果你的内存特别大,而且是线上,千万不要试着这时候分析内存,这样会瞬间吃光你的所有内存,机器停止响应,比内存OOM还要恐怖,必须强制重启机器才能解决,感觉是这个工具的问题

2.抓取内存快照

代码如下(示例):

const heapdump = require('heapdump')
process.chdir(config.snapshotdir);//config.snapshotdir设置快照存储路径
heapdump.writeSnapshot()

这个是比较靠谱的一种方法,也是我真正解决问题的途径,可以通过一条http请求或者一个websocket信令触发库快照抓取,也可以主动监控heapUsed的内存触发快照抓取。

快照抓取之后,就要用分析工具去分析内存中到底存储的什么数据了,首选chrome的快照分析工具如下图:

load之后就可以看到几个选项了:

 Shallow size就是对象本身占用内存的大小,Retained Size 就是该对象被 Gc 之后所能回收内存的总和,constructor 是各个数据结构的个数统计,

我们主要看Retained Size,利用自动排序,关注下内存比较大的数据结构,就可能是我们的内存泄漏点了

 与我们程序强相关的就是这几个对象了,明显需要释放却没有释放的,关键点来了,为什么没有释放,之前已经查过一遍了,从map或者array中已经删除了,到底是哪里还在引用导致没有释放呢,这就需要你去撸代码了,主要看哪里把这个对象引用了,但是此时的我还是一脸懵逼,不知道该从哪里去着手。

终于在网上发现一篇调试nodejs内存泄露的文章,想着三个臭皮匠顶个诸葛亮,借鉴下前人的力量,https://www.bookstack.cn/read/node-in-debugging/2.2heapdump.md,这段代码引起了我的注意:

这里以一段经典的内存泄漏代码作为测试代码:

const heapdump = require('heapdump')
let leakObject = null
let count = 0
setInterval(function testMemoryLeak() {
  const originLeakObject = leakObject
  const unused = function () {
    if (originLeakObject) {
      console.log('originLeakObject')
    }
  }
  leakObject = {
    count: String(count++),
    leakStr: new Array(1e7).join('*'),
    leakMethod: function () {
      console.log('leakMessage')
    }
  }
}, 1000)

为什么这段程序会发生内存泄漏呢?首先我们要明白闭包的原理:同一个函数内部的闭包作用域只有一个,所有闭包共享。在执行函数的时候,如果遇到闭包,则会创建闭包作用域的内存空间,将该闭包所用到的局部变量添加进去,然后再遇到闭包,会在之前创建好的作用域空间添加此闭包会用到而前闭包没用到的变量。函数结束时,清除没有被闭包作用域引用的变量。

这段代码内存泄露原因是:在 testMemoryLeak 函数内有两个闭包:unused 和 leakMethod。unused 这个闭包引用了父作用域中的 originLeakObject 变量,如果没有后面的 leakMethod,则会在函数结束后被清除,闭包作用域也跟着被清除了。因为后面的 leakObject 是全局变量,即 leakMethod 是全局变量,它引用的闭包作用域(包含了 unused 所引用的 originLeakObject)不会释放。而随着 testMemoryLeak 不断的调用,originLeakObject 指向前一次的 leakObject,下次的 leakObject.leakMethod 又会引用之前的 originLeakObject,从而形成一个闭包引用链,而 leakStr 是一个大字符串,得不到释放,从而造成了内存泄漏。

仿佛看到一丝黎明前的曙光有木有,不知道能不能和我们的代码产生化学反应呢?

然后再publisher的对象中找到了这么一句代码,原作者传入了this对象,他去干嘛,有没有释放呢?

 原作者的本意是给log增加一个固定的log头,这样就不用去每次打日志都去加一些公用的信息,但是log对象却是全局的,一直没有释放,导致一直绑定这个传入的对象,这也是pub和sub一直增长没有释放的真真原因,可谓是好心办坏事啊,困扰了几个月的问题,凶手就是这几行代码。

 

 


总结

本文只是简单的举了一个实际的项目中遇到的内存泄露的问题,感谢发达的网络,不然可能真的无从下手呢,很久都没有解决就是因为,一开始不知道怎么下手,想抓取快照,确是一直不能成功,服务器内存有限,导致一抓取就会导致OOM,最近抓取成功分析也就水到渠成了,学无止境,欢迎大神讨论

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要在Node.js中使用request库修改user-agent,你可以在发送请求之前设置请求头中的user-agent字段。以下是一个示例代码: ```javascript const request = require('request'); const options = { url: 'http://example.com', headers: { 'User-Agent': 'Your New User Agent' } }; request(options, function(error, response, body) { // 处理响应 }); ``` 在上面的代码中,你可以将`'Your New User Agent'`替换为你想要设置的新的user-agent字符串。然后,将`options`对象传递给`request`函数,以发送带有修改后user-agent的请求。 请注意,这只是一个示例代码,你需要根据你的实际需求进行修改和适应。 #### 引用[.reference_title] - *1* [NodeJs 发送request请求 设置Cookie](https://blog.csdn.net/fengxuenanhai/article/details/50923351)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [nodejs request设置proxy](https://blog.csdn.net/yuan882696yan/article/details/25052469)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LeeFeiLi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值