0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Node.js 内存泄漏问题初探

张康康 2018-11-01 13:39 次阅读

作者 | 后端Team朱捷峰

整理 | 包包

V8 垃圾回收机制

事实上,我们平时在写 Node.js 的时候很少去关心内存问题,那是因为 Node.js 对 Google V8 进行封装,底层的垃圾收回机制都交给 V8 处理。大部分时候,是不会有内存问题的。相对于 C/C++ 这类需要自己管理内存的语言,Node.js 有更加平滑的学习曲线,这也是 Node.js 最大的优势之一。但是也总有意外情况,可能导致 Node.js 进程内存泄漏。

那么如何避免我们的 Node.js 程序出现内存泄漏的情况呢?我们先来了解下 V8 内存管理机制。

一个进程通常是通过在内存中分配空间来体现的,这个空间我们称之为 Resident Set(常驻空间)。V8 将内存分为了以下几块:

• 代码区:实际正在运行的代码

• 栈区:包含了所有的值类型(数字、布尔值等)、指向存储在堆区的对象指针、定义程序控制流的指针

• 堆区:专门用来存储引用类型的内存区域,比如对象、字符串和闭包


在 Node.js 中,我们可以通过调用process.memoryUsage() 方法来来查询内存使用情况。该函数返回值如下:

memory usage

{

rss: 4935680,

heapTotal: 1826816,

heapUsed: 650472,

external: 49879

}

以上数值以字节为单位

• rss:表示 Resident Set 的大小

• heapTotal:表示堆的总大小

• heapUsed:表示堆的实际使用大小

• external:表示 V8 管理的绑定到 JavaScript 对象的 C++ 对象的大小

我们知道在 Node.js 的运行时中,JavaScript 是由 V8 编译成可执行的机器码。运行时的数据结构是由 V8 来管理的,我们能做的很有限。通过 JavaScript 我们是没法做到分配内存和释放内存的。

V8 的垃圾回收算法实现还是很复杂的,感兴趣的同学可以参考:http://newhtml.net/v8-garbage-collection/。但是我们仍然可以把原理简单抽象:如果一个内存片段没有被任何地方引用,我们可以假设它不再会被用到,那么该内存片段可以被释放。


上图表示在内存中各个对象的引用情况,只有当红球对象不再被任何对象引用的时候,它才能被回收。

异常情况

既然 V8 会进行垃圾回收,那我们为什么还要关心内存情况呢?

理想情况,内存占用会保持在一个相对稳定的范围:


实际上,我们仍然可能会看到内存占用升高的情况:


V8 垃圾回收机制尽可能地回收和释放内存,但是每次执行垃圾回收以后,内存占用仍然持续上升,这明显就是内存泄漏了。

制造内存泄漏

有一些很明显的情况会导致内存泄漏:1、比如将每位访客的 IP 记录在 global 上存储数组上;2、再比如著名的“ 沃尔玛内存泄漏事件”,它是由 Node.js 核心代码中一个遗漏的声明引发的血案,工程师们花了好几个星期去排查并最终得以解决。

在这篇文章里,我们就不一一列举所有可能产生问题的错误情况。我们来看一下一个难以排查的情况,代码很简单,你可以自己运行调试:

memory leak demo

const express = require('express');

const app = express();

const port = 3000;

let theThing = null;

const replaceThing = function () {

let originalThing = theThing;

let unused = function () {

if (originalThing)

console.log("hi");

};

theThing = {

longStr: new Array(1000000).join('*'),

someMethod: function () {

console.log(someMessage);

}

};

};

app.get('/leak', (req, res) => {

replaceThing();

let memoryInfo = JSON.stringify(process.memoryUsage());

console.log(memoryInfo);

res.send(memoryInfo);

})

app.listen(port, () => console.log(`Example app listening on port ${port}!`));

初看的话,这段代码没啥问题。我们可以想象 theThing 在每次调用 replaceThing() 时会被重写。问题就在于,someMethod 有闭包作用域作为上下文,这就意味着当调用 someMethod 时,unused 是可见的。虽然实际上 unused 并没有被调用,但是它却阻止了 V8 垃圾回收机制对 originalThing 进行回收。这就是我们平时所说的“循环引用”:


既然找到问题所在,那么如何解决呢?答案很简单,我们只要切断循环引用就可以了,这里我们只需要在 replaceThing 这个方法最后加入 theThing = null。

针对这个问题,我们还可以通过 ESLint 的 no-unused-vars 规则来避免定义了但是未使用的变量,这样可以减少循环引用的可能性。

排查问题

理解了垃圾回收的原理,那么我们平常在码代码的时候也要注意避免循环引用的情况出现。但是就像上面这种情况,有时候就是防不胜防。那么遇到问题的时候,我们应该如何排查呢?

推荐一下我写的一个小工具 heapsnapshot.js ,可以获取生成堆的快照信息,如下图:


然后利用 Chrome 开发者工具,Memory 来做具体分析:


请选择相邻的3个堆快照文件,导入 Memory 分析工具中,如下图:


第一步,先选择 Profiles 中的第二个文件,然后筛选 Objects 选项选择“Objects allocated between 1539255057342 and 1539255076968 ”,然后在 Constructor 中进行具体的分析 。


第二步,同理对第二个和第三个文件进行对比分析。找到两次分析都出现过的元素,重点排查,定位到具体的问题代码,再做修改。


第三步,重复上述过程,检查内存泄漏问题是否解决。

以上只是对 Node.js 内存问题的一个初步探讨,感兴趣的话推荐大家去看下 V8 垃圾回收的原理。平常我们在编码的时候也要注意尽量避免产生循环引用,但是如果遇到了也不要担心,可以通过上面的步骤排查解决。


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 内存
    +关注

    关注

    8

    文章

    3052

    浏览量

    74262
  • NODE.JS
    +关注

    关注

    1

    文章

    48

    浏览量

    32802
收藏 人收藏

    评论

    相关推荐

    Bun 1.2震撼发布:全力挑战Node.js生态的JavaScript运行时新星

    了与 Node.js 的兼容性,还为开发者带来了内置的数据库支持和云服务集成能力,进一步强化了其“全能工具包”的定位。Node.js 兼容性获得突破性进展在此次更新中,最引人注目的是 Bun 在 Node.js 兼容性
    的头像 发表于 01-24 10:42 85次阅读
    Bun 1.2震撼发布:全力挑战<b class='flag-5'>Node.js</b>生态的JavaScript运行时新星

    虚拟内存溢出该怎么处理 虚拟内存在服务器中的应用

    、虚拟内存溢出的原因 内存泄漏 :程序中未正确释放的内存会导致内存泄漏,随着时间的推移,这些
    的头像 发表于 12-04 09:49 256次阅读

    使用OpenVINO™ ElectronJS中创建桌面应用程序

      最近,我完成了一个 demo 演示,展示了 OpenVINO 在 Node.js 框架中的强大功能。得益于与 Electron.js 的集成,该演示不仅能够高效地执行神经网络推理,还提供了交互式
    的头像 发表于 11-25 11:35 248次阅读
    使用OpenVINO™ ElectronJS中创建桌面应用程序

    Node.js小科普和Node.js安装常见管理工具

    Node.js是一个JavaScript的运行环境,用来执行JavaScript代码。 为什么会出现这么一个运行环境呢,从JavaScript研发初衷可以看出它是为了运行在浏览器中的,让网页交互更加
    的头像 发表于 11-23 15:37 156次阅读
    <b class='flag-5'>Node.js</b>小科普和<b class='flag-5'>Node.js</b>安装常见管理工具

    前端技术探秘-Nodejs的CommonJS规范实现原理

    了解Node.js Node.js是一个基于ChromeV8引擎的JavaScript运行环境,使用了一个事件驱动、非阻塞式I/O模型,让JavaScript 运行在服务端的开发平台,它让
    的头像 发表于 11-05 11:56 851次阅读
    前端技术探秘-Nodejs的CommonJS规范实现原理

    工程师必备!Node.js和常见管理工具介绍(附操作演示)

    语言及其相关生态中的一些基础而关键的概念,以及它们是如何在互联网历史浪潮中产生的。一JavaScript与Node.js1JavaScriptJavaScript,即JS
    的头像 发表于 08-30 12:34 348次阅读
    工程师必备!<b class='flag-5'>Node.js</b>和常见管理工具介绍(附操作演示)

    ARMxy ARM 物联网边缘计算网关支持 Node-RED 用于工业控制

    在恶劣环境下稳定运行。丰富的通信接口(如网口、USB、RS485 等)及 WiFi/4G 支持,使网关能够灵活适应不同网络环境。 Node-RED 是一个基于 Node.js 的流程编排工具,通过
    的头像 发表于 08-19 17:01 432次阅读
    ARMxy ARM 物联网边缘计算网关支持 <b class='flag-5'>Node</b>-RED 用于工业控制

    如何检测内存泄漏

    检测内存泄漏是软件开发过程中一项至关重要的任务,它有助于识别和解决那些导致程序占用过多内存资源,从而影响程序性能甚至导致程序崩溃的问题。以下将详细阐述几种常见的内存
    的头像 发表于 07-30 11:50 2221次阅读

    NONOS 1.5.3/1.5.4 SSL内存泄漏的原因?

    我已经通过随附的代码验证了当发生 SSL 握手错误时,会生成内存泄漏 此外,espconn_reconnect_callback不称为信令ESPCONN_HANDSHAKE - TCP SSL 握手
    发表于 07-18 07:24

    使用system_show_malloc()检查内存泄漏遇到异常怎么解决?

    我想使用system_show_malloc()检查内存泄漏,但是当我调用该函数时,我得到了致命的异常: 致命异常 28 (LoadProhibitedCause): epc1
    发表于 07-10 06:32

    Node-RED如何制作漂亮的界面

    Node-RED不仅是一个强大的编程工具,还能通过其仪表盘(Dashboard)功能为物联网应用创建美观、实用的界面。以下是如何使用Node-RED制作漂亮界面的详细步骤和技巧。很多公司已经将产品
    的头像 发表于 06-26 16:50 5132次阅读
    <b class='flag-5'>Node</b>-RED如何制作漂亮的界面

    Node-RED安装本地教程

    Node-RED是一个基于流的开发工具,广泛应用于物联网(IoT)、家庭自动化和其他数据驱动的应用程序。它通过图形化的编程界面,使得非专业程序员也能轻松上手。本文将介绍如何在本地环境中安装Node-RED,帮助你快速开始项目开发。
    的头像 发表于 06-24 12:10 2246次阅读
    <b class='flag-5'>Node</b>-RED安装本地教程

    鸿蒙开发实战:网络请求库【axios】

    [Axios] ,是一个基于 promise 的网络请求库,可以运行 node.js 和浏览器中。本库基于[Axios]原库v1.3.4版本进行适配,使其可以运行在 OpenHarmony,并沿用其现有用法和特性。
    的头像 发表于 03-25 16:47 4010次阅读
    鸿蒙开发实战:网络请求库【axios】

    C语言内存泄漏问题原理

    内存泄漏问题只有在使用堆内存的时候才会出现,栈内存不存在内存泄漏问题,因为栈
    发表于 03-19 11:38 570次阅读
    C语言<b class='flag-5'>内存</b><b class='flag-5'>泄漏</b>问题原理

    【鸿蒙】webview内存泄漏问题的分析报告

    1 关键字 webview;内存泄漏 2 问题描述 问题现象:在 3.1release 版本和 3.2bete1 版本中,在 RK3568 上使用 etsWeb 和其他浏览器时,webview 所占
    的头像 发表于 03-02 15:12 2220次阅读