Node进阶 - 内存管理和垃圾回收
Node内存管理和垃圾回收
node.js 是基于 V8 引擎的 javascript 运行环境。
V8 引擎
javascript 代码运行的时候提供编译优化、内存管理、垃圾回收等功能
代码编译优化:
1、通过 parser 将 javascript 源码转为 ast 抽象语法树
2、将 ast 抽象语法树转为字节码,
3、然后转为机器可运行的汇编代码
内存管理:管理内存分配、内存划分
垃圾回收:通过垃圾回收机制对无用代码释放内存
内存管理
类型
常驻内存:运行 node 进程时候的所有内存( 代码占用内存、栈内存、堆内存、堆外内存 )
栈内存:用于存放变量( javascript 中基本类型 )
堆内存:用于存放对象、闭包引用上下文( 引用类型 )
堆外内存:不是通过 V8 分配,不受V8管理,不占用V8内存。一些用于存放 buffer 数据的 ( buffer 对象依然在堆内存中 )
限制
默认给堆内存分配的大小:64位系统为 1.4G、32位系统为 0.7G
可以通过 node 启动命令修改内存大小。
不过分配的内存越大,垃圾回收一次的时间越长
内存信息获取
node 提供了 process.memoryUsage() 获取当前进程的内存信息
rss :常驻内存大小( 进程分配的内存 )
heapTotal:给 V8 分配的内存大小
heapUsed:已使用的堆内存大小
external:堆外使用的内存( buffer )
1、内存溢出和堆外内存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | const fs = require( 'fs' ) const format = function (item, type = 0) { let ext = 'byte' switch (type) { case 1: ext = 'KB' break ; case 2: ext = 'M' break ; case 3: ext = 'G' break ; case 3: ext = 'T' break ; } if (item < 1024) return item.toFixed(2) + ext return format(item / 1024, type + 1) } function showMemory() { const memoryUsage = process.memoryUsage() console.log(`---------------------`) for (const item in memoryUsage) { console.log(`${item}:${format(memoryUsage[item])}`) } } showMemory() // 使用 40M 堆外内存 const buffer = new ArrayBuffer(40 * 1024 * 1024) const total = [] setInterval(() => { // 20M 内存的添加 total.push( new Array(20 * 1024 * 1024)) showMemory() },2000) |
垃圾回收
释放一些在应用程序中不在被引用,或者空指针的一些变量的内存。
1、变量为空的时候释放内存栈
2、对象结束没引用或者引用的对象被清除的时候释放对应内存( 对象无法被根节点访问时候 )
堆内存中的分类
新生代:内存比较小,未经历垃圾回收的对象存放位置
from 区:回收检查( 算法回收 ) 的时候,检查区
to 区:回收检查( 算法回收 ) 的时候,from检查到对象被引用存活了
老生代:内存是新生代20倍,from 区收检查( 算法回收 ) 的时候,已经经历过的对象或者to区满了时候送到老生代
新生代垃圾回收
新生代垃圾回收是通过交换 from 区和 to 区来实现的
1、新建的对象被存放在 from 区,满了之后执行新生代垃圾回收检查
2、检查 from 区对象,如果已经经历过一次检查且依然被引用的对象送到老生代区
3、检查 from 区对象,如果被引用则总到 to 区,to 区满了25%则送到老生代区
4、结束 from 检查后,交换 from 区和 to 区身份( from 区变 to区,反之 )
5、再次检查,回到 1。
老生代垃圾回收
老生代垃圾回收没有新生代频繁
主要通过判断是否被引用进行标记清除,然后对存活的对象整理
1、检查老生代区,是否被引用,未被引用,释放该内存块的内存
2、将依旧存活的对象,整理到老生代区的一端
3、清除存活一端以外部分的内存块
堆内存里面的对象主要是靠引用计数来判断是否释放内存(每引用一次增加1)
内存泄漏场景
1、未释放无用的全局变量/对象
2、闭包的上下文
1 2 3 4 5 6 7 8 9 10 11 12 13 | function add() { const x = 1; const y = 2; return function (a) { return y + 2 } } const add1 = add(); /* * 执行 add() 后 * x 将会被释放 * y 将存在 add1 的上下文中,不会被释放( 除非 add1 = null ) * */ |
3、缓存大量数据
1 2 | // 分成小的一部分一部分处理 const array = new Array(20 * 1024 * 1024) |
内存分析工具
利用内存检查工具( node-heapdump、node-profiler ) 生成内存快照
利用 chrome 进行分析。
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 理解Rust引用及其生命周期标识(下)
· 从二进制到误差:逐行拆解C语言浮点运算中的4008175468544之谜
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 2025成都.NET开发者Connect圆满结束
· 后端思维之高并发处理方案
· 千万级大表的优化技巧
· 在 VS Code 中,一键安装 MCP Server!
· 10年+ .NET Coder 心语 ── 继承的思维:从思维模式到架构设计的深度解析