JavaScript原理--内存管理

106 阅读6分钟

不管什么样的编程语言,在其代码执行的过程中都是需要为其分配内存的。但是不同的编程语言对内存的申请和释放会有不同的实现,主要分为手动和自动管理内存

  • 手动管理内存:像C、C++等一些接近底层的编程语言,都是需要手动来申请和释放内存。
  • 自动管理内存:像Java、JavaScript、Python等一些高级编程语言,都是自动帮助我们管理内存的。

内存生命周期

不管什么样的编程语言,以及它用什么方式来管理内存,其内存的管理都具备以下的生命周期

  • 分配内存:分配其需要的内存。
  • 使用内存:使用分配的内存。
  • 释放内存:使用完毕后,对其进行释放。

在 JavaScript 中,

  1. 当我们创建变量、函数或任何你能想到的东西时(对象),JS 引擎会为此分配内存并在不再需要时释放它。
  2. 分配内存是在内存中保留空间的过程,而释放则释放空间,准备用于其他目的。
  3. 每次我们分配一个变量或创建一个函数时,它的内存总是经历以下相同的阶段
  • 分配内存 JavaScript 为我们解决了这个问题:它为我们创建的对象分配了我们需要的内存。
  • 使用内存 使用内存是我们在代码中明确执行的操作:读取写入内存只不过是从变量读取或写入变量。
  • 释放内存 此步骤也由 JavaScript 引擎处理。一旦分配的内存被释放,它就可以用于新的目的。

内存管理上下文中的“对象”不仅包括 JS 对象,还包括函数和函数作用域。

内存空间

  • 栈内存(stack):

    是一块连续的内存区域,每个区块按照一定次序存放(后进先出)

    所有原始数据类型都存储在栈内存中

  • 堆内存(heap):

    1. 是不连续的内存区域,即数据可以任意存放, 主要存放的是对象等。
    2. 引用数据类型会在堆内存中开辟一个空间,并且会有一个十六进制的内存地址,在栈内存中声明的变量的值就是十六进制的内存地址。
    3. 函数也是引用数据类型,我们定义一个函数的时候,会在堆内存中开辟空间,会以字符串的形式存储到堆内存中去。

    JavaScript 中所有变量首先指向堆栈。如果它是非原始值,则堆栈包含对堆中对象的引用。举个栗子

    const name = 'curry' // 为字符串分配内存
    const age = 30 // 为数字分配内存const info = { // 这会在堆中创建一个新对象,并在堆栈中创建对它的引用。
      name: 'kobe',
      age: 24
    }
    

image.png

为什么要区分栈、堆

变量主要是两种形式

  • 一种内容短小(比如一个int整数),需要频繁访问,但是生命周期很短,通常只在一个方法内存活
  • 一种内容可能很多,可能不需要太频繁的访问,但生命周期较长,通常很多个方法中可能都要用到

那么自然将这两类变量分开就显得比较理性。

  • 一类存储在栈区,通常是局部变量、操作符栈、函数参数传递和返回值

    栈区就像临时工,干完就跑,所以超快,

    缺点也很多,比如生命周期短,一般只能在一个方法内存活,通常最大栈区可用内存都很小,你不可能往栈区里堆很多数据。

  • 一类存储在堆区,通常是较大的结构体(或者OOP中的对象)、需要反复访问的全局变量。

    堆区就是各种慢,申请内存慢,访问慢,修改慢,释放慢,整理慢(或者说GC垃圾回收)

    优点也不言而喻,访问随机灵活,空间超大,在不超可用内存的情况下你要多大就给多大。

静态内存分配,故空间小动态内存分配 ,故空间大
连续的内存区域不连续的内存区域:
反应快反应慢

内存分配

JavaScript是自动管理内存的,所以在我们编写JS代码定义变量时就会为其分配内存。

根据JavaScript不同的数据类型,会对其分配到不同的内存空间中,数据类型主要分为基本数据类型复杂数据类型

  • 对于基本数据类型的内存分配会在执行时,直接在栈空间中进行分配。

    基本数据类型(也称值类型):string、number、boolean、undefined、null、symbol

  • 对于复杂数据类型的内存分配会在堆内存中开辟一块空间,变量引用其内存地址

    复杂数据类型(也称引用类型):object、function、array

JS的数组的内存

传统语言数组的概念

像 C/C++ 这种传统的编译型的语言,它们的数组在内存中用一串连续的区域来存放一些值,而且它们的数组中存放的数据类型都需要预先设定成同一类型

JS数组

JS数组中存储的数据类型是可以不一样的;意味着,JS 数组中内存地址不是连续的。

因为传统语言数组下标取值,就是因为是连续,且每小块大小都相等的内存空间,可以直接对应上内存的地址。

而 JS 数组可以存放不同类型的数据,但它也能进行数组下标取值。这其实因为采用的是哈希映射的方式,获取到对应数组下标的值的。

不过,现在的 JS 引擎为了优化 JS 的性能,它会分配一个连续的内存空间给存储了相同数据类型的数组,以达到更好的遍历效果。所以,只要你数组里存的是相同类型的值,在内存中的地址还是连续的。

知识支撑

静态/动态内存

  • 静态内存: 为这些值分配了固定数量的内存,所以原始值的大小是有限制的

  • 动态内存: 引擎不会分配固定数量的内存。相反,将根据需要分配更多空间。


最后一句
学习心得!若有不正,还望斧正。