Skip to content
快速预览

JS中的堆(Heap)栈(Stack)内存

✍️ w 🕒 2023-06-28 12:51:11(a year ago) 🔗 A.前端知识整理

JavaScript 是动态语言,那么什么是静态语音呢,我们把在运行过程中需要检查数据类型的语言称为动态语言 let a = 1在声明变量之前需要先定义变量类型。我们把这种在使用之前就需要确认其变量数据类型的称为静态语言。int a = 1

有些语言,会例如把 bool 类型的变量赋值给数字类型的变量,我们通常把这种偷偷转换的操作称为隐式类型转换支持隐式类型转换的语言称为弱类型语言,不支持隐式类型转换的语言称为强类型语言

图 1

因此 js 具备的特点就是一种弱类型的、动态的语言

  • 弱类型,意味着你不需要告诉 JavaScript 引擎这个或那个变量是什么数据类型,JavaScript 引擎在运行代码的时候自己会计算出来。
  • 动态,意味着你可以使用同一个变量保存不同类型的数据。

知道JavaScript 是什么语言后在来看如何分配内存的。 创建变量、函数等行为的时候JS 引擎会为此分配内存,并在不再需要时释放它,每一次它们都会经历三个阶段 分配内存 -- 使用内存 -- 释放内存

  1. 分配内存:在JavaScript中,当我们创建变量、函数等对象时,JS引擎会自动为这些对象分配内存空间。这意味着我们不需要手动管理内存分配。

  2. 使用内存:使用内存是指我们在代码中对变量进行读取和写入操作。这是我们在编写程序时需要明确处理的部分。

  3. 释放内存:当我们不再需要某个对象时,JavaScript引擎会自动释放其占用的内存。这样,这部分内存空间就可以被用于其他目的。这一过程是由JavaScript引擎自动处理的,我们不需要手动进行内存释放。

关于储存的位置JavaScript 引擎有两个地方可以存储数据'堆'和'栈'

原始类型占据的空间是在内存中分配的;对象类型占据的空间是在内存中分配的

栈 -- Stack

  1. 栈内存是计算机内存中的一部分,由操作系统直接管理。它的大小由操作系统决定,通常比堆内存要小。栈内存主要用于存储基本数据类型(如string、number、bigint、boolean、undefined、null和symbol)和函数调用时的临时数据(如局部变量和函数参数)。

  2. 基本数据类型和指向对象和函数的引用(内存地址)在栈内存中存储。这些数据的值大小是固定的,编译时就可以确定。因此,在程序执行之前,系统会为这些数据分配固定数量的内存。这种分配内存的方式称为静态内存分配。静态内存分配的优点是内存管理简单,访问速度快;缺点是不适合存储动态变化的数据。

  3. null是一种特殊的数据类型,表示空值或没有值。虽然typeof null返回的是'object',但实际上null并不是一个对象。这是因为JavaScript中的一个历史遗留问题,最初的设计中null被认为是一个特殊的对象值,表示没有对象。后来,虽然null的概念已经发生了变化,但typeof null的返回值仍然保持为'object'

  4. 栈具有以下特点

    • 操作数据快:栈内存中的数据操作都是在栈顶进行的,所以速度较快。每次数据入栈或出栈,只需要移动栈顶指针,操作简单高效。
    • 数据必须是静态的:栈内存中存储的数据大小在编译时就已经确定,不能动态改变。这意味着栈内存不适合存储动态变化的数据,如数组和对象。
    • 内存管理简单:栈内存的管理由操作系统完成,不需要程序员手动管理。操作系统会自动分配和回收栈内存,降低了内存泄漏的风险。
    • 栈大小有限:栈内存空间有限,如果数据过多可能会发生栈溢出(Stack Overflow)错误。栈溢出通常是由于递归调用过深或者局部变量过多导致的。为了避免栈溢出,可以使用堆内存存储大量数据或者限制递归调用的深度。

栈的声明过程

let myNumber = 23 赋值过程经历三个步骤 储存的空间都在栈中

  • 为变量创建一个唯一标识符('myNumber')。
  • 在内存中分配一个地址(将在运行时分配)。基本类型(如数字、字符串、布尔值等)也需要在内存中分配地址来存储它们的值
  • 变量 myNumber 被分配了一个内存地址,该地址用于存储值 (23) 中存储一个值。

图 1

栈的赋值过程

js
// 根据打印结果可以看出虽然myNumber 赋值给 newVar 但 当给myNumbe 加 1的时候并没有影响newVar 
// 说明栈基本类型赋值时候是重新开辟了空间
let myNumber = 23
let newVar = myNumber
myNumber = myNumber + 1
console.log(myNumber ) // 24
console.log(newVar  )  // 23

myNumber 虽然赋值给了newVar 但是newVar 依旧在栈中新开辟了自己的空间记录自己

图 2

看字符串的案例

js
// js中的原始数据类型,因此将分配新的内存地址,abcd存储在那里,并将mystring指向这个新的内存地址
let myString = 'abc'
myString = myString + 'd'

当我们给之前变量重新赋值时候如图并不是在之前基础上的地址直接加上'd' 而是开辟了新的地址,为什么是新开辟地址,在开篇说过栈中的数据有一个共同点。这些数据的大小是固定的,如果从原来的'abc' 内存大小直接变成'abcd' 完全不符合这点相反却变成了动态内存,因此在栈中的每一次变化都是一次新的空间开辟

图 3

堆 -- Heap

区分和数据结构堆的概念,内存管理中的堆主要关注动态分配和释放内存,而数据结构中的堆主要关注数据的组织和访问

数据结构中的堆(Heap) :在数据结构中,堆是一种特殊的树形数据结构,通常用于实现优先队列。这种堆满足堆属性,即每个节点的值都大于(或小于)其子节点的值。最常见的堆数据结构是二叉堆(Binary Heap),它是一种完全二叉树,可以用数组表示。

内存管理中的堆(Heap) :在内存管理中,堆是一种动态分配内存的区域,用于存储程序运行时创建的对象和函数。这种堆通常在程序的整个生命周期内存在,并且可以在程序的多个线程之间共享。在这种情况下,堆是与栈(Stack)相对应的内存区域,栈用于存储程序的局部变量和函数调用的上下文。

堆(Heap)是计算机内存中的一种数据结构,用于存储动态分配的内存。在 JavaScript 中,堆主要用于存储对象和函数。与栈(Stack)相比,堆具有以下特点:

  • 操作速度慢,但容量大:堆的操作速度相对较慢,因为在堆中查找、分配和释放内存需要更多的时间。然而,堆的容量通常比栈大得多,因此可以存储更多的数据。

  • 可以将动态大小的数据存储在此处:堆中的内存是动态分配的,这意味着在程序运行时可以更改对象的大小。这对于处理不确定大小的数据(如用户输入的数据)非常有用。

  • 堆在应用程序的线程之间共享:堆中的数据可以在应用程序的不同线程之间共享。这使得多个线程可以访问和操作相同的数据,从而实现线程间的通信。

  • 堆管理起来比较困难:由于堆的动态特性,管理堆中的内存分配和释放相对复杂。程序员需要确保正确分配和释放内存,以避免内存泄漏和其他内存管理问题。

  • 值大小没有限制:堆中存储的值没有大小限制,这意味着可以在堆中存储任意大小的对象和函数。这为处理大型数据结构(如图、树等)提供了便利。

堆 变量声明

let myArray= [] 赋值过程经历四个步骤对 象的引用地址在栈中,数据存在堆中

  1. 为变量创建一个唯一标识符('myArray'),这个标识符将用于在代码中引用这个变量。

  2. 在内存中分配一个地址,这个地址将用于存储变量的值。这个地址在运行时动态分配。

  3. 将分配的内存地址指向堆内存。堆内存是用于存储对象和数组等复杂数据类型的内存区域。

  4. 在内存地址指向的堆内存中存储一个空数组([])。这个空间是动态的,意味着数组的大小和内容可以在运行时改变。

如图 myArray 变量声明的表示符号指向栈中一个地址这个地址对应栈中的value 实际是对象堆的内存地址图 4

堆 变量赋值

js原始类型的赋值会完整复制变量值,而引用类型的赋值是复制引用地址,当变量赋值给另一个变量的时候内存地址指向其实是同一个。

js
const reference = [1];
const refCopy = reference;
reference.push(2);

图 5

当变量重新赋值时候改变的是内存地址指向

js
let obj = { first: 'reference' };  
obj = { second: 'ref2' }

图 6

通过其他的模型图解来看

图 7

图 8

参考

浏览器工作原理与实践_李兵

在 Javascript 中解释值与引用

# JavaScript 内存详解 & 分析指南

# JavaScript's Memory Management Explained

# JavaScript 的内存模型

# V8中的JavaScript的内存管理与垃圾回收

javascript-fundamentals-call-stack-and-memory-heap

memory-life-cycle-heap-stack-javascript/

https://www.javascripttutorial.net/javascript-call-stack/

Released under the MIT License.