Nodejs AI 考拉技术分享会-node.js 内存模型

发布于 1周前 作者 wuwangju 来自 nodejs/Nestjs

Nodejs AI 考拉技术分享会-node.js 内存模型

前言

有关 node 的三大模型:内存-并发-异步,在近期都会慢慢分享给大家。这部分的分享会,考拉最萌程序猿 Nick 已经开了线下分享会给考拉 dev 小伙伴啦~ 现在将分享内容整理并放上来,希望能给这里的搬砖 noder 带来更多精神粮食! 本文尝试理清 js 内存模型的相关知识点,鉴于 js 的教程非常丰富,这里就不重复写了,只建立个知识索引就好了,详细知识看文末的参考文章即可。

栈与堆

基础数据类型存在栈中,对象存储在堆中

  1. 基础数据类型
    Undefined
    Null
    Boolean
    Number
    String

  2. 引用类型 Object、Function、Array 和自定义的对象,可以看做是指针。指针是存在栈中,但是指向的变量在堆中

引用类型.png

下面代码表现了基础类型和引用类型的区别

// demo01.js
var a = 20;
var b = a;
b = 30;
// 这时 a 的值是多少? // 20
// demo02.js
var m = { a: 10, b: 20 }
var n = m;
n.a = 15;
// 这时 m.a 的值是多少? // 15

执行上下文

概念

每次当控制器转到 ECMAScript 可执行代码的时候,就会进入到一个执行上下文。可执行代码的类型包括:

  • 全局代码( Global code ) 这种类型的代码是在"程序"级处理的:例如加载外部的 js 文件或者本地<script></script>标签内的代码。全局代码不包括任何 function 体内的代码。 这个是默认的代码运行环境,一旦代码被载入,引擎最先进入的就是这个环境。
  • 函数代码( Function code )
  • Eval 代码( Eval code )

执行栈 demo 执行栈.png

建立的细节

  1. 创建阶段 [当函数被调用,但未执行任何其内部代码之前]
    创建作用域链( Scope Chain )
    创建变量,函数和参数
    求” this “的值

  2. 执行阶段

初始化变量的值和函数的引用,解释 /执行代码。

我们可以将每个执行上下文抽象为一个对象,这个对象具有三个属性

ECObj: {
    scopeChain: { /* 变量对象( variableObject )+ 所有父级执行上下文的变量对象*/ }, 
    variableObject: { /*函数 arguments/参数,内部变量和函数声明 */ }, 
    this: {} 
}

变量对象

变量对象( Variable object )是说 JS 的执行上下文中都有个对象用来存放执行上下文中可被访问但是不能被 delete 的函数标示符、形参、变量声明等。它们会被挂在这个对象上。

代码示例

var color = 'blue';

function changeColor() { var anotherColor = ‘red’;

function swapColors() {
    var tempColor = anotherColor;
    anotherColor = color;
    color = tempColor;
}

swapColors();

}

changeColor();

闭包概念

MDN 对闭包的定义为: 闭包是指那些能够访问自由变量的函数。

那什么是自由变量呢? 自由变量是指在函数中使用的,但既不是函数参数也不是函数的局部变量的变量。

由此,我们可以看出闭包共有两部分组成: 闭包 = 函数 + 函数能够访问的自由变量。 举个例子:

var a = 1;

function foo() { console.log(a); }

foo();

foo 函数可以访问变量 a,但是 a 既不是 foo 函数的局部变量,也不是 foo 函数的参数,所以 a 就是自由变量。
那么,函数 foo + foo 函数访问的自由变量 a 就构成了一个闭包
js 不会销毁被闭包引用的对象

GC 垃圾回收

Garbage Collection 垃圾回收是一种自动的内存管理机制。当一个电脑上的动态内存不再需要时,就应该予以释放,以让出内存,这种内存资源管理,称为垃圾回收。

新生代和老生代内存分区

为什么要分区?为了 GC 效率

新生代的 GC 算法

Scavenge 算法,它将堆内存一分为二,将存活对象在从空间 1 复制到空间 2,其他对象被回收。特点是速度快。新生代内存的对象过大或者存活时间过长就会去到老生代内存。

老生代的 GC 算法

Mark-Sweep 标记清除算法,标记清除回收之后,内存会变得碎片化。 Mark-Compact 标记整理算法,在整理过程中,将活着的对象往一端移动,移动完成后,直接清理掉边界外的内存。

内存泄露

本质上,内存泄漏可以定义为:应用程序不再需要占用内存的时候,由于某些原因,内存没有被操作系统或可用内存池回收。编程语言管理内存的方式各不相同。只有开发者最清楚哪些内存不需要了,操作系统可以回收。一些编程语言提供了语言特性,可以帮助开发者做此类事情。另一些则寄希望于开发者对内存是否需要清晰明了。

排除方法

  1. 抓下内存快照,使用 chrome 分析,使用框架和各种库的时候干扰项非常多
  2. alinode

参考文章

重要

  1. 深入理解闭包(五)——作用域、作用域链和执行上下文
  2. 深入理解 JavaScript 闭包 [译]
  3. 深入理解 JavaScript 执行上下文、函数堆栈、提升的概念
  4. MDN 函数
  5. JavaScript 深入之闭包
  6. 轻松排查线上 Node 内存泄漏问题
  7. 4 类 JavaScript 内存泄漏及如何避免 介绍了如何使用 chrome dev tool 排查内存泄露

不重要

  1. 解读 V8 GC Log (二): 堆内外内存的划分与 GC 算法
  2. Node 性能优化
  3. 解读 V8 GC Log (一): Node.js 应用背景与 GC 基础知识
  4. NodeJS 中被忽略的内存
  5. 前端基础进阶(二):执行上下文详细图解
  6. JavaScript 内存模型

分享内容出自考拉程序猿 Nick 的 blog:http://myfjdthink.com/


1 回复

针对“Node.js AI考拉技术分享会——Node.js内存模型”的帖子,以下是一个专业回复:

Node.js的内存模型主要基于Chrome V8引擎,其内存管理对于性能至关重要。V8引擎使用分代垃圾回收机制,包括新生代和老生代,分别处理短生命周期和长生命周期的对象。

在Node.js中,内存主要分为堆内存(Heap)、栈内存(Stack)和原生内存(Native)。堆内存存放JavaScript对象和闭包等,栈内存存放基本类型变量和对象的指针等,而原生内存则涉及Node.js C++层面的内存使用,如Buffer。

以下是一个简单的内存使用监控示例,使用Node.js内置的process.memoryUsage()方法:

setInterval(() => {
  const memoryUsage = process.memoryUsage();
  console.log(`内存使用情况:\n${JSON.stringify(memoryUsage, null, 2)}`);
}, 10000);

此代码每10秒输出一次内存使用情况,包括RSS(常驻集大小)、堆总大小、堆已使用大小等信息。

内存泄漏是Node.js应用中常见的问题,常见的内存泄漏包括全局变量引用、未清理的闭包、定时器和监听器等。因此,在开发过程中应尽量避免全局变量,确保闭包只保留必要引用,及时清理定时器和监听器。

总之,深入理解Node.js内存模型,有助于开发高效稳定的Node.js应用。

回到顶部