JS 垃圾回收

    在 JS 中 值类型数据存储在 栈空间中,引用类型的数据存储在堆空间中。有些数据被使用之后,就不需要了,我们需要将这些 垃圾数据 进行回收从而释放内存空间,防止这些垃圾数据堆积在 内存中。

    JS 如何 回收垃圾

        在 JS 中,垃圾数据 由 垃圾回收器 来释放,并不需要手动通过代码释放。数据主要存储在栈 和 堆 中,所以我们分两种情况。

    1 调用栈中的数据回收

代码示例

        调用栈详情

调用栈详情

    当执行到 bar() 这段代码的时候,此时的调用栈 情形如图,此时有个一记录当前执行状态的指针(称为 ESP),指向 bar 函数执行上下文, 记录当前 正在执行到的 地方。当 bar 函数执行完, JS 引擎会 销毁 bar 函数执行上下文,指针移动到上一个 执行上下文,也就是 foo 函数的执行上下文,这个 向下移动的操作就是 销毁 bar 函数执行上下文的过程

1
2

      总结:一个函数执行结束之后,JavaScript引擎会通过向下移动ESP来销毁该函数保存在栈中的执行上下文。


    2 堆中数据的回收

        当 foo 函数执行完成后, ESP 就指向了 全局执行上下文,bar 函数执行上下文 和 foo 函数执行上下文 都被销毁,但是 堆 当中的 两块内存依然 存储着数据。要回收堆当中的数据,就要用到 JS 中的垃圾回收器了。

        在垃圾回收中有一个重要的概念:代际假说,所有的垃圾回收策略都是建立在该假说之上的。   

        代际假说的两个特点: 1. 大部分对象在内存中存在的时间很短,简单来说,就是很多对象⼀经分配内存,很快就变得不可访问; 2. 不死的对象,会活得更久。

        垃圾回收算法很多,但不是 一种就能处理所有的情况,需要根据不同的情况,采取不同的算法,所以 JS 把 堆分为 新生代老生代 两个区域, 新生代存放的是生存时间短的对象,老生代存放的生存时间久的对象。

        新生代 通常只支持 1-8M 的容量,老生代的容量就大很多。 JS 使用两种不同的垃圾回收器来处理这两块区域的垃圾回收。

        1. 副垃圾回收器,主要处理新生代的垃圾回收;

        2. 主垃圾回收器,主要负责老生代的垃圾回收。

    垃圾回收的工作流程

        1. 标记空间中活动对象(还在使用的对象)和 非活动对象(可以进行垃圾回收的对象);

        2. 回收非活动对象所占的内存,就是在所有标记完成后,统一清除内存中所有被标记为可回收的对象;

        3. 内存整理。通常情况下,在进行垃圾回收后,内存中就会出现 大量不连续的空间,称为 内存碎片。当内存中出现大量 内存碎片后,如果需要分配较大连续内存的时候,就有可能出现内存不足的情况。所以我们需要整理这些 内存碎片(有些垃圾回收器不会产生内存碎片,所以这步骤是可选操作)。

        副垃圾回收器

            主要负责新生区的垃圾回收。通常情况下,大多数小的对象会被分配到 新生区,所以这个区域大小不大,但是操作频繁。

            新生区中 采用 Scavenge 算法,就是把 新生区对半划分为两个区域,一半是对象区域,一半是空闲区域

堆空间

            新加入的对象都会存放到对象区域,当对象区域要被写满的时候,就需要执行一次垃圾清理。

            在垃圾回收过程中,首先对 对象区域 内的垃圾做标记,标记完成后,进入垃圾清理阶段, 副垃圾回收器 会把 还在使用的对象 复制到空闲区域,并且有序的排列起来,相当于完成了碎片的整理,复制完成后 空闲区域就没有了内存碎片。

            完成复制后,对象区域 和 空闲区域 角色进行交换,原来的对象区域变成空闲区域,原来的空闲区域变成对象区域,这样就完成了垃圾回收操作,同时这种 角色的对调 能让新生区的这两块区域无限重复使用下去。

            由于 新生区采用了 Scavenge 算法,每次执行清理操作,都要进行一次复制,如果新生区空间设置太大,那么 复制的时间成本会变大,清理时间就会更久,所以 为了执行效率,一般新生区的空间会被设置的比较小。但也就是因为空间小,所以 还在使用的对象很容易存满整个区域,为了解决这个问题, JS 采用了 对象晋升策略,就是 经过两次垃圾回收依然存活的对象,会被移到老生区

        主垃圾回收器

             主要负责老生区的垃圾回收。除了新生区晋升的对象,一些大的对象会被分配到老生区,因此,老生区的对象有两个特点:占用空间大 存活时间长

             由于老生区的对象比较大,采用 Scavenge 算法在复制阶段 会导致 消费大量时间,效率不高,同时还会浪费一半的空间。所以,主垃圾回收器 采用 标记 - 清除(Mark-sweep)的算法进行垃圾回收。

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

3

            当 bar 函数执行结束,ESP 向下移动执行 foo 函数执行上下文,这时候遍历 调用栈,不会找到 引用 1002地址的遍历,编辑为垃圾数据,1001地址有变量在引用,标记为活动对象。

        2. 垃圾清除过程,当采用 标记-清除法后,会产生 大量不连续的内存碎片,会导致无法分配到足够的连续内存,于是就产生另一种算法:标记-整理(Mark - Compact),这个过程是将 所有 还在使用的对象 都移动到一端,然后直接清除掉边界之外的内存。

标记清楚和标记整理

全停顿

    V8 使用 副垃圾回收器 和 主垃圾回收器处理垃圾回收, 不过 JS 是单线程,一旦执行 垃圾回收算法,其他 JS 脚本都会暂停,等待垃圾回收结束才恢复执行,我们称这种行为 为 全停顿(Stop-The-World)。

        在 新生区 中的垃圾回收,因其空间小,且 活动对象少,所以全停顿的影响不但,但是 老生区不一样。如果垃圾回收 占用时间越久,那么这个过程中,主线程会被完全占用,不能处理其它事情,比如 页面正在执行 动画,因为垃圾回收器,导致 动画在一段时间无法执行,会使页面出现卡顿现象。

全停顿

            为了降低 老生区的垃圾回收造成的卡顿,V8 将标记过程分为一个个子标记过程,同时让垃圾回收标记 和 JS 应用逻辑交替进行,直到标记阶段完成,我们把这个算法称为 增量标记(Incremental Marking)算法

增量标记

            采用 增量标记,能将一个完整的垃圾回收任务拆分成很多小任务,这些小任务执行时间较短,可以穿插在其他 JS 任务中执行,比如 动画效果,就不会使页面出现卡顿。


总结

    JS 中的 简单数据类型 和 引用数据类型 分别存放在 栈 和 堆 中;

    栈中的数据 通过 ESP 向下移动 销毁保存在 栈中的数据;

    堆分为两块区域:新生区 和 老生区,主要通过 副垃圾回收器 和 主垃圾回收器 处理垃圾;

    副垃圾回收器 采用 Scavenge 算法 将 新生区分为 对象区域 和 空闲区域,通过两个区域不断替换角色来无限使用;

    主垃圾回收器 采用标记清除法,标记整理法,增量标记法 进行垃圾回收;

    无论 主 副垃圾回收器,流程都是 标记 ----  清除 ----- 整理 这几个步骤。

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

推荐阅读更多精彩内容

  • 和C#、Java一样JavaScript有自动垃圾回收机制,也就是说执行环境会负责管理代码执行过程中使用的内存,在...
    fortunatelys阅读 337评论 0 1
  • 垃圾回收机制 1. 概述 JS的垃圾回收机制是为了以防内存泄漏。内存泄漏:当已经不需要某块内存时这块内存还存在着,...
    yiyi呐阅读 219评论 0 0
  • 什么叫垃圾回收呢?我的理解就是在js的作用域中变量使用完之后我们需要将他占用的内存进行释放,然后供别的变量使用,降...
    青春前行阅读 228评论 0 0
  • https://zhuanlan.zhihu.com/p/272519496[https://zhuanlan.z...
    罗不错阅读 342评论 0 0
  • 使用JavaScript进行前端开发时几乎完全不需要关心内存管理问题,对于前端编程来说,V8限制的内存几乎不会出现...
    写Blog不取名阅读 10,899评论 9 20