JavaScript面试考点之垃圾回收机制

我们知道,程序运行中会有一些垃圾数据不再使用,需要及时释放出去,如果我们没有及时释放,这就是内存泄露。JavaScript 是一门具有自动垃圾收集机制的编程语言,由执行环境负责在代码执行时管理内存。JS 中的垃圾数据都是由垃圾回收(Garbage Collection,缩写为 GC)器自动回收的,不需要手动释放。JS 引擎中有一个后台进程称为垃圾回收器,它监视所有对象,观察对象是否可被访问,然后按照固定的时间间隔周期性的删除掉那些不可访问的对象即可。

常见的垃圾回收方法:1)引用计数方法;2)标记清除方法。

1)引用计数

引用计数就是给一个占用物理空间的对象附加一个引用计数器,当有其它对象引用这个对象时,这个对象的引用计数加一,反之解除时就减一,当该对象引用计数为 0 时就会被回收。如果出现循环引用,则一直不会被回收,还是会造成内存泄漏。

我们大部分人时刻都在写着循环引用的代码,看下面这个例子:

我们为一个元素的点击事件绑定了一个匿名函数,我们通过event参数是可以拿到相应元素el的信息的。el有一个属性onclick引用了一个函数(其实也是个对象),函数里面的参数又引用了el,这样el的引用次数一直是2,即使当前这个页面关闭了,也无法进行垃圾回收。

解决办法:及时销毁绑定的事件、使用弱引用 weakMap、weakSet。

2)标记清除

V8 中主垃圾回收器就采用标记清除法进行垃圾回收。主要流程如下:

标记:遍历调用栈,看老生代区域堆中的对象是否被引用,被引用的对象标记为活动对象,没有被引用的对象(待清理)标记为垃圾数据。

清除:将所有垃圾数据清理掉。

垃圾回收算法

垃圾回收的实现简单分为以下三个步骤:

1)可访问性

从 GC Roots 对象出发,遍历 GC Root 中的所有对象:

a、可访问对象:通过 GC Root 遍历到的对象,我们就认为该对象是可访问的(reachable),那么必须保证这些对象应该在内存中保留。

b、不可访问对象:通过 GC Roots 没有遍历到的对象,则是不可访问的(unreachable),并会对其做上标记,那么这些不可访问的对象就可能被回收。

GC Root 有很多,通常包括了以下几种 (但是不止于这几种):全局的 window 对象(位于每个 iframe 中);文档 DOM 树,可以通过遍历文档到达的所有原生 DOM 节点组成;存放栈上变量

2)回收不可访问对象所占据的内存

在所有的标记完成之后,统一清理内存中所有被标记为可回收的对象。

3)内存整理

频繁回收对象后,内存中就会存在大量不连续空间,称为内存碎片。当出现了大量的内存碎片之后,如果需要分配较大的连续内存时,就会出现内存不足的情况,所以最后一步需要整理这些内存碎片。

在我们的实际开发过程中,如果我们想要让垃圾回收器回收某一对象,就将对象的引用直接设置为 null。但如果一个对象被多次引用时,例如作为另一对象的键、值或子元素时,将该对象引用设置为 null 时,该对象是不会被回收的,依然存在。

如果想让 a 置为 null 时,该对象被回收,该怎么做?ES6 考虑到了这一点,推出了WeakMap 。它对于值的引用都是不计入垃圾回收机制的,所以名字里面才会有一个"Weak",表示这是弱引用(对对象的弱引用是指当该对象应该被GC回收时不会阻止GC的回收行为)。

map
weakmap

从上面的例子我们可以看出,只要外部的引用消失,WeakMap 内部的引用,就会自动被垃圾回收清除。

WeakMap 对象是一组键值对的集合,其中的键是弱引用对象,而值可以是任意

注意,WeakMap 弱引用的只是键名,而不是键值。键值依然是正常引用。

WeakMap 中,每个键对自己所引用对象的引用都是弱引用,在没有其他引用和该键引用同一对象,这个对象将会被垃圾回收(相应的key则变成无效的),所以,WeakMap 的 key 是不可枚举的。

除了 WeakMap 还有 WeakSet 都是弱引用,可以被垃圾回收机制回收,可以用来保存DOM节点,不容易造成内存泄漏。另外还有 ES12 的 WeakRef。

WeakMap vs Map(详情见 https://www.jianshu.com/p/114ec4a13e4e)

JavaScript 引擎 V8 - 垃圾回收

目前 V8 采用了两个垃圾回收器,主垃圾回收器副垃圾回收器。在 V8 中,会把堆分为新生代(新生代通常只支持 1~8M 的容量)和老生代(容量大)两个区域,新生代中存放的是生存时间短的对象,老生代中存放生存时间久的对象。分别对新老生代采用不同的垃圾回收算法来提高效率,对象最开始都会先被分配到新生代(如果新生代内存空间不够,直接分配到老生代),新生代中的对象会在满足某些条件后,被移动到老生代,这个过程叫晋升(当一个对象经过多次复制仍然存活时,它就会被认为是生命周期较长的对象。这种较长生命周期的对象随后会被移动到老生代中,采用新的算法进行管理)。

对象晋升的条件主要有两个:

a、对象从对象区复制到空闲区时,会检查它的内存地址来判断这个对象是否已经经历过一次Scavenge回收。如果已经经历过了,会将该对象从新生空间移动到老生代空间中,如果没有,则复制到空闲区。总结来说,如果一个对象是第二次经历从对象区复制到空闲区,那么这个对象会被移动到老生代中

b、当要从对象区复制一个对象到空闲区时,如果空闲区已经使用了超过25%,则这个对象直接晋升到老生区中。设置25%这个阈值的原因是当这次Scavenge回收完成后,这个空闲区会变为对象区,接下来的内存分配将在这个空间中进行。如果占比过高,会影响后续的内存分配。

1)副垃圾回收器

负责新生代的垃圾回收,大多数小的对象都会被分配到新生代,垃圾回收比较频繁。新生代中的垃圾数据用 Scavenge 算法来处理。Cheney算法将内存一分为二,叫做semispace,分为两个区域:对象区域 ,空闲区域。

垃圾回收过程:新加入的对象都会存放到对象区域,当对象区域快被写满时,就需要执行一次垃圾清理操作。

a、垃圾标记和清理:首先要对对象区域中的垃圾做标记;标记完成之后,就进入垃圾清理阶段。副垃圾回收器会把这些我们仍然在用的对象复制到空闲区域中,同时它还会把这些对象有序地排列起来,在复制过程,相当于完成了内存整理操作,复制后空闲区域就没有内存碎片了。

b、角色翻转:完成复制后,进行角色翻转。把原来的对象区变成空闲区,把原来的空闲区变成对象区。是为了让活跃对象始终保持在一块semispace中,另一块semispace始终保持空闲的状态。

2)主垃圾回收器

负责老生代中的垃圾回收,大多数占用空间大、存活时间长的对象都会被分配到老生代里。老生代中的垃圾数据用标记 - 清除算法进行垃圾回收,因为老生代中的对象通常比较大,复制大对象非常耗时,会导致回收执行效率不高,所以采用标记清除法。

垃圾回收过程:

a、标记:标记阶段就是从一组根元素开始,递归遍历这组根元素,在这个遍历过程中,能到达的元素称为活动对象,没有到达的元素就可以判断为垃圾数据。

b、清除:它和副垃圾回收器的垃圾清除过程完全不同,主垃圾回收器会直接将标记为垃圾的数据清理掉。

c、整理:清除后会产生大量不连续的内存碎片,过多的碎片会导致大对象无法分配到足够的连续内存,于是需要引进另一种算法:标记 - 整理,整理这些内存碎片。

由于 JavaScript 是运行在主线程之上的,在垃圾回收时会阻塞 JavaScript 脚本的执行,会造成页面卡顿等问题。

为了解决上述问题,V8 团队推出了并行、并发和增量等垃圾回收技术。

a、将一个完整的垃圾回收的任务拆分成多个小的任务,解决单个垃圾回收时间长的问题。

b、将标记对象、移动对象等任务转移到后台线程进行,减少主阻塞线程的时间。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容