简单来聊聊js的垃圾回收机制

这是我参与更文挑战的第24天,活动详情查看: 更文挑战

前言

最近看到一个面试题,题目是:

你能介绍一下js的垃圾回收机制吗?

之前也看过一个垃圾回收的内容,但是很容易就忘了,今天借着这次机会,好好复习下,争取如果被问到,能够回答出来。

垃圾回收

什么是垃圾回收? (简称GC: Garbage Collection)

我们在写代码时声明变量,js引擎需要内存来存储这些变量。对于我们不再使用的变量,js引擎会定期找到这些变量,然后清除,释放内存,这个过程就叫垃圾回收。这个过程时是js引擎自动进行,我们无需手动回收处理。

举个例子说明下:

let name = '答案cp3'
name = ''
复制代码

首先先声明name等于答案cp3,这样在栈里面开启一个内存,存储name,值是答案cp3
然后下一行把name给赋值'',这样后name等于''答案cp3就没有变量使用了,js引擎就会把它当作垃圾回收了

再来看看

let obj1 = {name: '答案cp3'}
let obj2 = obj1
obj1 = null
复制代码

请问这个会垃圾回收吗?

答案是不会。 我们在栈内存存了一个变量obj1,指向堆的值{name: '答案cp3'}, 这时候把obj1赋值给obj2,相当于又在栈内存存了一个变量obj2,指向的是同一个堆; 然后这时候把obj1赋值为null,中断了obj1与堆的联系,但是此时obj2还指向堆的值,所以不会垃圾回收。

那js引擎怎么知道哪些变量是不再使用了?

主要是有2种方法:标记清除法引用计数法

标记清理法

这个是js引擎比较常用的。先标记,再清除。

流程是:

  • 通过root节点开始,层层遍历,给遍历到的对象都打上标记(证明还在引用);
  • 再次遍历,发现没有标记的对象,认为它是垃圾,然后把它回收。

引用计数法

js引擎记录了对象的引用次数,如果引用次数为0,则代表没有引用了,可以垃圾回收了。

var obj = {name: '答案cp3'} // {name: '答案cp3'} 引用次数加一
var obj1 = obj // 引用次数加一
var obj2 = obj // 引用次数加一,此时为3

var obj = obj1 = obj2  = null  {name: '答案cp3'} 引用次数为0, 等待垃圾回收
复制代码

引用计数法不用对所有对象遍历打标记,只需要看引用次数为0就可以清除了。效率高,但是如果引用的次数很多的话,可能会占据很大空间,还有一个问题,无法清除循环引用。因为循环引用的引用次数都不为0。

var obj = {}
var obj1 = {}
obj.a = obj1
obj1.a = obj
复制代码

可以看到,obj和obj1相互引用,引用次数不为0,无法清除。

优化方法

垃圾回收正常是会阻塞js代码的执行,但是一般是无感的,但是如果回收的对象过大可能会造成页面卡顿,所以会有一些优化方法,尽量让这个过程更顺滑;

垃圾回收优化的方法:

一般有增量回收分代回收闲时回收

  • 增量回收:js引擎会把回收的过程分成各个小部分,每次只执行一部分。
  • 分代回收:js引擎把数据分成新对象和老对象,新对象包括函数内部的变量,块作用域变量等,老对象包括全局变量,DOM等,然后把堆分成新生代和老生代两块区域,新对象放到新生代中,老对象放到老生代中,采用不同的算法来回收。
  • 闲时回收:垃圾回收只在cpu空闲时工作,以减少阻塞。

内存泄漏

正常是不再使用的内存,应该会被回收的,但是由于某种原因无法被释放,导致一直占据着内存,这就叫内存泄漏。

会触发内存泄漏的行为:

没声明的全局变量

function test () {
  name = '答案cp3'
}
test()
复制代码

正常函数调用后变量会很快被回收,但是此处没有加声明,导致这个变量没有被回收。

事件监听未移除

比如我们在当前页面对window监听事件,这时候页面跳转了或者移除了,如果window事件没有移除,那就可能还在监听,但是新页面是不需要的,导致一直占据着内存。

定时器

比如我们要每隔一秒更新页面的倒计时,如果倒计时到了,我们把页面的DOM清除了,但是定时器没有清除,就会导致定时器一直在运行,无法回收。

闭包

function test () {
    var obj = {name: '答案cp3'}
    return obj
}
info = test()
复制代码

如果info在使用后没有清除或者info一直没有使用,导致obj一直未被释放。

WeakSet,WeakMap对垃圾回收的影响

es6新增了WeakSetWeakMap两种数据结构,WeakSet成员必须是对象,WeakMap键名必须是对象,然后他们都是弱引用的,就是如果对象一旦没有被其它地方使用,那么对象就会自动消失,无需进行垃圾回收。

主要是为了解决对象没有被使用了,如果忘了清除,导致一直占据着内存导致内存泄漏的问题。

var map = new Map ()
var obj = {}
map.set(obj, true)

var wp = new WeakMap ()
var obj = {}
wp.set(obj, true)
复制代码

使用Map数据结构,如果obj没有其它地方使用了,则还会占据着内存,Map还会访问到

使用WeakMap数据结构,如果obj没有其它地方使用了,因为是弱引用,则键名obj会自动消失,WeakMap无法访问到了

总结

以上就是我总结的js的垃圾回收机制,简单来聊聊,算是复习和巩固了一下,希望能对你们有帮助~

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享