九、垃圾回收:垃圾数据是如何自动回收的?
发布日期:2021-05-14 15:28:39 浏览次数:22 分类:精选文章

本文共 1533 字,大约阅读时间需要 5 分钟。

调用栈中的数据是如何回收的

首先是调用栈中的数据,我们通过一段示例代码的执行流程来分析其回收机制。考虑如下代码:

function foo() {
var a = 1;
var b = { name: "极客邦" };
function showName() {
var c = 2;
var d = { name: "极客时间" };
}
showName();
}
foo();

当执行到第 6 行代码时,调用栈和堆空间状态如下:


从图中可以看出,原始类型的数据被分配到栈中,引用类型的数据会被分配到堆中。当 foo 函数执行结束之后,foo 函数的执行上下文会从堆中被销毁掉,那么它是怎么被销毁的呢?

当执行到 showName 函数时,JavaScript 引擎会创建 showName 函数的执行上下文,并将 showName 函数的执行上下文压入到调用栈中。 ESP 指针指向当前执行状态的地址。

showName 函数执行完成之后,JavaScript 会通过移动 ESP 来销毁 showName 函数执行上下文。上移或下移 ESP 呢?其实是下移。执行流程如下:

  • showName 函数执行结束
  • ESP 下移到 foo 函数的执行上下文

这样,showName 的执行上下文保存在栈中被销毁。ESP 作为运行时的内存管理器,通过指针的移动状态来决定对象的存活状态。


堆中的数据是如何回收的

foo 函数执行结束后,showNamefoo 函数的执行上下文已经从堆中销毁,但堆中的对象仍然存在。例如,1003 和 1050 这两个对象虽然不再引用,但仍然存在内存中。如何回收这些堆中的垃圾数据呢?

答案是,JavaScript 垃圾回收器负责回收。


垃圾回收器的工作流程

垃圾回收过程可以分为以下几个步骤:

  • 标记空间中活动对象和非活动对象
  • 回收非活动对象所占据的内存
  • 内存整理
  • 具体来说,Suffix垃圾回收器采用 Scavenge 算法,对新生代进行垃圾回收。


    新生代垃圾回收器

    新的垃圾回收器将新生代的内存空间分为两个区域:

    • 对象区:存储对象
    • 空闲区:空闲内存

    垃圾回收时,将对象区复制到空闲区,之后交换角色,空闲区变成对象区,对象区变成空闲区。

    这样,通过上述过程,无需递归操作就能高效回收垃圾数据。

    垃圾回收之所以在新生代设置较小的内存空间,是因为这部分对象占用内存较少,回收频繁且时间成本较低。


    老生代垃圾回收器

    老生代的对象存在较长时间,使用标记 - 清除算法进行垃圾回收。

  • 标记
  • 清除
  • 具体步骤:

    标记阶段:从根元素开始遍历对象图,标记活动对象为灰色,非活动对象为黑色。标记过程通常采用增量标记,避免卡顿。

    清除阶段:收集所有黑色对象,将其所占用内存释放。

    标记 - 清除算法存在一个问题:会产生大量空闲内存,导致内存碎片。解决方案是采用标记 - 整理算法,集中回收内存碎片。


    全停顿问题

    使用标记 - 清除算法,必须暂停 JavaScript 执行进行垃圾回收,这种现象称为 全停顿

    在老生代垃圾回收时,垃圾回收运算符会使得主线程暂停,导致应用程序卡顿。

    事实上,V8引擎的全停顿时间可能高达 200 毫秒,这对用户体验至关重要。为了降低老生代的卡顿问题,V8 推升了增量标记技术。


    代际假说与回收策略

    解决全停顿问题的关键在于对内存进行分类管理。Ek新生代老生代 的划分基于对象生存周期的长短。新生代对象存活时间短,老生代存活时间长。

    这种划分使得垃圾回收可以根据对象特性采用不同的算法,从而提高回收效率。这样才能在不影响用户体验的前提下,实现高效垃圾回收。

    上一篇:七、JavaScrip 的执行上下文
    下一篇:六、“重排”“重绘”和“合成”

    发表评论

    最新留言

    网站不错 人气很旺了 加油
    [***.192.178.218]2025年04月27日 16时02分47秒

    关于作者

        喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
    -- 愿君每日到此一游!

    推荐文章