目录
堆内存
新生代与老生代
新生代的 Scavenge 算法
Mark-Sweep 和 Mark-Compact
查看内存相关信息
测试最大内存限制
首页 web前端 js教程 聊聊Node.js中的 GC (垃圾回收)机制

聊聊Node.js中的 GC (垃圾回收)机制

Nov 29, 2022 pm 08:44 PM
前端 node.js

Node 是如何做 GC (垃圾回收)的?下面本篇文章就来带大家了解一下。

聊聊Node.js中的 GC (垃圾回收)机制

GC,Garbage Collection,垃圾回收。在编程中,一般指的是内存自动回收机制,会定时将不需要用到的数据进行清除。

Node.js 底层使用了 V8 引擎。V8 是 Google 开源的一款高性能 JavaScript 引擎,使用了 C++ 进行编写。【相关教程推荐:nodejs视频教程

Node.js 的内存主要分成三部分:

  • 代码空间:存放代码段的地方;

  • 栈:函数调用栈产生的临时变量,为一些基本类型,比如数字、字符串、布尔值,以及对象引用(保存的是地址,不保存对象本身)。

  • 堆:存放对象等数据;

堆内存

Node.js 底层使用的是 V8,下面讲解一下 V8 的内存回收机制。

首先 JS 中所有的对象都会保存在堆内存中。在创建进程的时候,会分配一个初始大小的堆内存,然后我们的对象就会放到里面。

当对象越来越多,堆内存会不够用,此时堆内存会动态地扩大。如果到达一个最大限制(现在通常是 4GB),就会堆内存溢出的错误,然后终止 Node.js 进程。

新生代与老生代

V8 首先将内存分成两部分,或者说两个生代(generation):

  • 新生代(yong generation):保存一些存活时间较短的对象;

  • 老生代(old generation):保存存活时间长或者长驻的对象。

新生代很小,这里会存放一些存活时间很短的对象,通常它们会被频繁地回收(比如函数的调用栈的一些临时对象)。

新生代可通过 node --max-semi-space-size=SIZE index.js 修改新生代的大小,单位为 MB。

另外,老生代则通过 --max-old-space-size=SIZE 来设置

新生代的 Scavenge 算法

新生代使用了 Scavenge 算法,是一种基于 copy(复制)的算法。

新生代会分成两个空间,这种空间称为 semispace,它们为:

  • From 空间:新声明的对象会放入这里

  • To 空间:用作搬移的空间

新声明的对象会放入到 From 空间中,From 空间的对象紧密排布,通过指针,上一对象紧贴下一个对象,是内存连续的,不用担心内存碎片的问题。

所谓内存碎片,指的是空间分配不均匀,产生大量小的连续空间,无法放入一个大对象。

当 From 空间快满了,我们就会遍历找出活跃对象,将它们 copy 到 To 空间。此时 From 空间其实就空了,然后我们将 From 和 To 互换身份。

如果一些对象被 copy 了多次,会被认为存活时间较长,将被移动到老生代中。

这种基于 copy 的算法,优点是可以很好地处理内存碎片的问题,缺点是会浪费一些空间作为搬移的空间位置,此外因为拷贝比较耗费时间,所以不适合分配太大的内存空间,更多是做一种辅助 GC。

Mark-Sweep 和 Mark-Compact

老生代的空间就比新生代要大得多了,放的是一些存活时间长的对象,用的是 Mark-Sweep (标记清除)算法。

首先是标记阶段。从根集 Root Set(执行栈和全局对象)往上找到所有能访问到的对象,给它们标记为活跃对象。

标记完后,就是清除阶段,将没有标记的对象清除,其实就是标记一下这个内存地址为空闲。

这种做法会导致 空闲内存空间碎片化,当我们创建了一个大的连续对象,就会找不到地方放下。这时候,就要用 Mark-Compact(标记整理)来将碎片的活跃对象做一个整合。

Mark-Compact 会将所有活跃对象拷贝移动到一端,然后边界的另一边就是一整块的连续可用内存了。

考虑到 Mark-Sweep 和 Mark-Compact 花费的时间很长,且会阻塞 JavaScript 的线程,所以通常我们不会一次性做完,而是用 增量标记 (Incremental Marking)的方式。也就是做断断续续地标记,小步走,垃圾回收和应用逻辑交替进行。

另外,V8 还做了并行标记和并行清理,提高执行效率。

图片

查看内存相关信息

我们可以通过 process.memoryUsage 方法拿到内存相关的一些信息。

process.memoryUsage();
登录后复制

输出内容为:

{
  rss: 35454976,
  heapTotal: 7127040,
  heapUsed: 5287088,
  external: 958852,
  arrayBuffers: 11314
}
登录后复制

说明

  • rss:常驻内存大小(resident set size),包括代码片段、堆内存、栈等部分。

  • heapTotal:V8 的堆内存总大小;

  • heapUsed:占用的堆内存;

  • external:V8 之外的的内存大小,指的是 C++ 对象占用的内存,比如 Buffer 数据。

  • arrayBuffers:ArrayBufferSharedArrayBuffer 相关的内存大小,属于 external 的一部分。

以上数字的单位都是字节。

测试最大内存限制

写一个脚本,用一个定时器,让一个数组不停地变大,并打印堆内存使用情况,直到内存溢出。

const format = function (bytes) {
  return (bytes / 1024 / 1024).toFixed(2) + " MB";
};

const printMemoryUsage = function () {
  const memoryUsage = process.memoryUsage();
  console.log(
    `heapTotal: ${format(memoryUsage.heapTotal)}, heapUsed: ${format(
      memoryUsage.heapUsed
    )}`
  );
};

const bigArray = [];
setInterval(function () {
  bigArray.push(new Array(20 * 1024 * 1024));
  printMemoryUsage();
}, 500);
登录后复制

需要特别注意的是,不要用 Buffer 做测试。

因为 Buffer 是 Node.js 特有的处理二进制的对象,它不是在 V8 中的实现的,是 Node.js 用 C++ 另外实现的,不通过 V8 分配内存,属于堆外内存。

我使用电脑是 macbook pro M1 Pro,Node.js 版本为 v16.17.0,使用的 V8 版本是 9.4.146.26-node.22(通过 process.versions.v8 得到)。

输出结果为(省略了一些多余的信息):

heapTotal: 164.81 MB, heapUsed: 163.93 MB
heapTotal: 325.83 MB, heapUsed: 323.79 MB
heapTotal: 488.59 MB, heapUsed: 483.84 MB
...
heapTotal: 4036.44 MB, heapUsed: 4003.37 MB
heapTotal: 4196.45 MB, heapUsed: 4163.29 MB

<--- Last few GCs --->

[28033:0x140008000]    17968 ms: Mark-sweep 4003.2 (4036.4) -> 4003.1 (4036.4) MB, 2233.8 / 0.0 ms  (average mu = 0.565, current mu = 0.310) allocation failure scavenge might not succeed
[28033:0x140008000]    19815 ms: Mark-sweep 4163.3 (4196.5) -> 4163.1 (4196.5) MB, 1780.3 / 0.0 ms  (average mu = 0.413, current mu = 0.036) allocation failure scavenge might not succeed


<--- JS stacktrace --->

FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
...
登录后复制

可以看到,是在 4000 MB 之后超出了内存上限,发生堆溢出,然后退出了进程。说明在我的机器上,默认的最大内存为 4G。

实际最大内存和它运行所在的机器有关,如果你的机器的内存大小为 2G,最大内存将设置为 1.5G。

更多node相关知识,请访问:nodejs 教程

以上是聊聊Node.js中的 GC (垃圾回收)机制的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

PHP与Vue:完美搭档的前端开发利器 PHP与Vue:完美搭档的前端开发利器 Mar 16, 2024 pm 12:09 PM

PHP与Vue:完美搭档的前端开发利器在当今互联网高速发展的时代,前端开发变得愈发重要。随着用户对网站和应用的体验要求越来越高,前端开发人员需要使用更加高效和灵活的工具来创建响应式和交互式的界面。PHP和Vue.js作为前端开发领域的两个重要技术,搭配起来可以称得上是完美的利器。本文将探讨PHP和Vue的结合,以及详细的代码示例,帮助读者更好地理解和应用这两

如何使用 Go 语言进行前端开发? 如何使用 Go 语言进行前端开发? Jun 10, 2023 pm 05:00 PM

随着互联网技术的发展,前端开发变得日益重要。尤其是移动端设备的普及,更需要高效、稳定、安全又易维护的前端开发技术。而作为一门快速发展的编程语言,Go语言已经被越来越多的开发者所使用。那么,使用Go语言进行前端开发行得通吗?接下来,本文将为你详细说明如何使用Go语言进行前端开发。先来看看为什么使用Go语言进行前端开发。很多人认为Go语言是一门

前端面试官常问的问题 前端面试官常问的问题 Mar 19, 2024 pm 02:24 PM

在前端开发面试中,常见问题涵盖广泛,包括HTML/CSS基础、JavaScript基础、框架和库、项目经验、算法和数据结构、性能优化、跨域请求、前端工程化、设计模式以及新技术和趋势。面试官的问题旨在评估候选人的技术技能、项目经验以及对行业趋势的理解。因此,应试者应充分准备这些方面,以展现自己的能力和专业知识。

Django是前端还是后端?一探究竟! Django是前端还是后端?一探究竟! Jan 19, 2024 am 08:37 AM

Django是一个Python编写的web应用框架,它强调快速开发和干净方法。尽管Django是一个web框架,但是要回答Django是前端还是后端这个问题,需要深入理解前后端的概念。前端是指用户直接和交互的界面,后端是指服务器端的程序,他们通过HTTP协议进行数据的交互。在前端和后端分离的情况下,前后端程序可以独立开发,分别实现业务逻辑和交互效果,数据的交

C#开发经验分享:前端与后端协同开发技巧 C#开发经验分享:前端与后端协同开发技巧 Nov 23, 2023 am 10:13 AM

作为一名C#开发者,我们的开发工作通常包括前端和后端的开发,而随着技术的发展和项目的复杂性提高,前端与后端协同开发也变得越来越重要和复杂。本文将分享一些前端与后端协同开发的技巧,以帮助C#开发者更高效地完成开发工作。确定好接口规范前后端的协同开发离不开API接口的交互。要保证前后端协同开发顺利进行,最重要的是定义好接口规范。接口规范涉及到接口的命

golang可以做前端吗 golang可以做前端吗 Jun 06, 2023 am 09:19 AM

golang可以做前端,Golang是一种通用性很强的编程语言,可以用于开发不同类型的应用程序,包括前端应用程序,通过使用Golang来编写前端,可以摆脱JavaScript等语言引起的一系列问题,例如类型安全性差、性能低下,以及代码难以维护等问题。

Go语言前端技术探秘:前端开发新视野 Go语言前端技术探秘:前端开发新视野 Mar 28, 2024 pm 01:06 PM

Go语言作为一种快速、高效的编程语言,在后端开发领域广受欢迎。然而,很少有人将Go语言与前端开发联系起来。事实上,使用Go语言进行前端开发不仅可以提高效率,还能为开发者带来全新的视野。本文将探讨使用Go语言进行前端开发的可能性,并提供具体的代码示例,帮助读者更好地了解这一领域。在传统的前端开发中,通常会使用JavaScript、HTML和CSS来构建用户界面

前端怎么实现即时通讯 前端怎么实现即时通讯 Oct 09, 2023 pm 02:47 PM

实现即时通讯的方法有WebSocket、Long Polling、Server-Sent Events、WebRTC等等。详细介绍:1、WebSocket,它可以在客户端和服务器之间建立持久连接,实现实时的双向通信,前端可以使用 WebSocket API来创建WebSocket连接,并通过发送和接收消息来实现即时通讯;2、Long Polling,是一种模拟实时通信的技术等等

See all articles