JavaScript 垃圾回收机制

2023-03-15  本文已影响0人  Avery_G

前言

我们知道,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)

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

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

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

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

上一篇下一篇

猜你喜欢

热点阅读