美文网首页
JavaScript 垃圾回收机制

JavaScript 垃圾回收机制

作者: Avery_G | 来源:发表于2023-03-15 15:14 被阅读0次

    前言

    我们知道,JavaScript 中的变量分为两种:基本类型和引用类型。基本类型的值存储在栈内存,引用类型的值存储于栈内存和堆内存,栈内存中保存的是堆内存地址,地址指向堆内存中保存着的具体的值。栈内存中的变量值使用完之后会自动出栈被立即回收,但是堆内存中的值则需要某种策略或手动回收。
    JavaScript 自带一套内存管理引擎来进行内存分配以及垃圾回收,那么为了更好的开发和维护高性能的 JavaScript 代码,我们需要对垃圾回收机制进行一些了解。
    下面我们从以下几个方面来了解垃圾回收机制

    • 什么是垃圾回收
    • 垃圾是怎么产生的
    • 垃圾回收机制

    什么是垃圾回收

    垃圾回收机制的原理即找到不再使用的变量,释放其内存,因此,垃圾收集器会按照固定的时间间隔(或代码执行中预定的收集时间),周期性地执行这已操作。

    垃圾是怎么产生的

    当一个对象没有任何变量或属性对它进行引用时,意味着我们无法在操作该对象了,这种对象就是所谓的"垃圾",此时就需要进行垃圾回收,如果不进行清理,内存占用越来越高,就会影响性能,甚至会导致进程崩溃。
    比如:

    let obj = [ 'a', 'b', 'c' ]
    obj = {
      name: 'abc'
    }
    

    在 JavaScript 中,引用类型的数据保留在堆内存中,栈内存中会保留一个地址,这个地址即为堆内存中保存的值。
    上面先声明了一个变量,为一个数组,之后,我们把这个变量重新赋值,那么之前的引用关系就没有了,此时,之前的那个数组就会失去引用关系,也就是我们无法在操作它了,它就变成了 "垃圾",等待被回收。

    垃圾回收机制

    最常见的垃圾回收机制有两种:

    1. 标记清除
    2. 引用计数

    标记清除(Mark-Sweep)

    标记清除,简单来说,就是使用某种方法,将不再使用的变量,也就是无用变量标记出来,等待垃圾收集器回收,释放内存。
    那么,垃圾收集器是如何标记无用变量呢,这里,我们先了解一个概念——可达性。

    可达性

    可达性,指的是变量从"根"出发,经过一层或多层可以被访问到。即一个变量从"根"出发,可以被访问到,那么它就是"可达"的,垃圾回收器将这些"可达"的变量视为有用变量,反之则视为无用变量,无用变量会被打上标记,便于之后回收。
    在 JavaScript 中,有一些基本的固有可达值,如:

    1. 本地函数的局部变量和参数
    2. 当前嵌套调用链上的其他函数的变量和参数
    3. 全局变量
    4. 一些其他的内部的值

    举一个简单的例子:

    let user = {
      name: 'Avery'
    }
    
    可达性.png

    这里是一个全局变量 user 引用了对象,当我们把 user 重写,那么这个引用就没有了,没有了引用就变成了不可达的,就会被回收


    不可达.png

    了解了可达性,我们就来看下垃圾回收器是如何进行标记的吧,各个浏览器的具体实现不太相同,其运行机制如下:

    • 变量进入上下文,会被加上标记,证明其存在于该上下文
    • 将所有在上下文的变量以及上下文中被访问引用的变量标记去掉,表明这些变量活跃
    • 之后再被加上标记的变量为准备删除的变量,因为上下文中已访问不到它们了
    • 执行内存清理,销毁所有带标记的非活跃值并收回之前被占用的内存

    标记清除有一个缺点就是-内存碎片,因为标记清除,在清除之后,剩余内存的位置是不变的,所以会导致空闲的内存空间是不连续的,大小不同的碎片。
    想要解决这个缺点,就需要标记整理,它会在标记结束后,将不需要清理的对象移至内存的一端,最后清理掉边界的内存。

    引用计数(Reference Counting)

    引用计数,把"对象是否不再需要"简化定义为"对象有没有其他对象引用到它",即该对象没有被任何对象或变量引用(零引用),就会被垃圾回收机制回收,其运行机制如下:

    • 当声明了一个变量,并赋予它一个引用的值时,该值的引用次数 +1
    • 当同一个值被赋值给另一个变量的时候,引用次数 +1
    • 当该变量被另一个值覆盖的时候,引用次数 -1
    • 当引用次数为 0 的时候,就会被内存回收

    引用计数有一个最大的问题,就是循环引用,如下:

    function test () {
      let a = {}
      let b = {}
      a.c = b
      b.c = a
    }
    

    如上,两个都互相引用了,引用计数不为 0 ,所以无法被回收,这样就会造成内存泄漏。

    相关文章

      网友评论

          本文标题:JavaScript 垃圾回收机制

          本文链接:https://www.haomeiwen.com/subject/offoldtx.html