垃圾回收机制:垃圾数据是如何回收的

在数据使用之后,并且不在被需要的数据称为垃圾数据,对这些垃圾数据需要进行回收来释放有限的内存空间。

垃圾回收有分为手动回收自动回收两种策略。

在Javascript中产生的垃圾数据是由垃圾回收器来回收的。

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

在 JavaScript 中,垃圾回收是由浏览器的 JavaScript 引擎负责的。这个引擎会周期性地扫描内存,查找不再被引用的对象,然后释放这些对象占用的内存。当一个函数执行完毕,它的执行上下文就会被标记为不再需要,这时候就可能触发垃圾回收。

js 复制代码
function foo() { 
    var a = { name: '竹合'}; 
    function bar() { 
        var b = { name: '竹合'}; 
    } 
    bar(); 
} 
foo();

在代码执行的时候还有一个记录当前状态的指针称为ESP

当执行到bar()函数的时候,ESP就指向了这个函数,当执行完之后,函数执行流程就到了foo函数,也就需要销毁bar函数的执行上下文,JS就会将ESP下移到foo函数的执行上下文,这个下移过程就是销毁bar函数的执行上下文的过程。

二、堆中的数据是如何回收的

垃圾回收机制主要有两种策略:

1. 标记清除(Mark and Sweep):

这是 JavaScript 最常用的垃圾回收策略之一。它的基本原理是在执行上下文中标记那些仍然被引用的变量,然后清除未被标记的变量。这通常是通过追踪从全局对象开始的引用链来完成的。

2. 引用计数:

这种策略会为每个值都维护一个引用计数,当这个值的引用计数变为零时,说明它不再被引用,可以被回收。然而,引用计数策略容易出现循环引用的问题,因为如果两个对象互相引用,它们的引用计数永远不会变为零。

JavaScript 引擎中的垃圾回收器会在适当的时候自动运行。具体的触发时机和实现方式可能因浏览器引擎而异。一般来说,垃圾回收器会在空闲时执行,确保不会对页面性能产生明显的影响。

大部分现代浏览器主要采用标记-清除(Mark and Sweep) 作为其主要的垃圾回收策略。

标记清除的基本原理是通过标记那些仍然被引用的变量,然后清除未被标记的变量。垃圾回收器会从全局对象开始,通过引用链遍历对象,标记那些仍然被引用的对象,然后清除那些未被标记的对象。这个过程确保了内存中只保留了仍然可达的对象,不再被引用的对象就可以被安全地回收。

JavaScript 引擎中的具体垃圾回收实现可能会有一些优化和改进,以提高性能和减少对页面的影响。例如,V8 引擎(Chrome 使用的 JavaScript 引擎)使用了一种称为增量标记的技术,以减小垃圾回收的停顿时间。其他引擎也可能采用类似的优化策略。

V8 引擎中增量标记的基本工作流程(主要用在老生代中):

1. 初始标记阶段(Initial Marking):

  • 在这个阶段,V8 首先执行一次快速的初始标记,标记那些直接与根对象关联的对象。这个阶段的目标是尽快完成,以减小初始的停顿时间。

2. 并发标记阶段(Concurrent Marking):

  • 在并发标记阶段,V8 引擎启动后台线程,该线程负责继续标记其余的对象,但不会阻塞主线程执行 JavaScript 代码。这样,应用程序可以在并发标记的同时继续运行,减小了对用户体验的影响。

3. 重标记阶段(Re-Marking):

  • 在并发标记完成后,V8 需要执行最后的重标记步骤,确保在并发标记期间发生变化的对象也被正确标记。这一步会导致短暂的停顿。

4. 清理阶段(Clean-Up):

  • 清理阶段用于清理不再被引用的对象,释放它们占用的内存。这一阶段通常也是并发执行的。

回收堆中的垃圾数据就需要用到JS中的垃圾回收器。

代际假说(Generational Hypothesis) 是垃圾回收领域的一种理论,它提出了一个观点:大多数对象在内存中存在的时间很短,而只有一小部分对象会长时间存活。基于这个观点,代际假说提出了一种优化垃圾回收的方法,即将对象分为不同的代际,并根据它们的存活时间采用不同的回收策略。

代际假说的核心思想包括以下两点:

1. 新生代(Young Generation):

大多数对象在被创建后很短时间内就会被销毁,而只有一小部分对象会存活更长的时间。因此,将对象划分为新生代和老生代两个部分,新生代包含大部分短时间存活的对象。

2. 分代回收策略:

针对不同代际的对象采用不同的回收策略。新生代的对象使用一种较为频繁但简单的回收算法,例如复制算法(Copying Algorithm) ,以迅速清除短寿命的对象。而老生代的对象使用更复杂的回收算法,例如标记-清除(Mark and Sweep)算法,以处理存活时间较长的对象。

代际假说的优势在于,通过将对象分为新生代老生代,并使用不同的回收策略,可以更有效地提高垃圾回收的性能。大部分对象很快就会被清理,只有一小部分对象需要经历更复杂的回收过程。这使得垃圾回收可以更快地完成,减小了对应用性能的影响。

在V8引擎中会把堆分为新生代和老生代两个区域。新生代用来存放生存时间短的对象(通常支持1~8M的容量),老生代用来存放生存时间久的对象。

V8引擎也分别使用两个不同的垃圾回收器:

  1. 副垃圾回收器,负责回收新生代的垃圾。

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

1. 垃圾回收器的工作流程

两种类型的垃圾回收器共用一套共同的流程。

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

  2. 然后回收非活动对象所占据的内存。在所有标记完成之后,统一清理内存中的所有被标记可以回收的对象。

  3. 最后是内存整理:频繁回收对象之后,内存中会存在大量不连续空间(内存碎片)。当内存中出现了大量碎片,如果需要分配大量连续内存,就有可能出现内存空间不足的情况。

2. 副垃圾回收器

新生代用Scavenge算法来处理,也就是将新生代空间对半划分为两个区域,一半是对象区域,一半是空闲区域。

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

在垃圾回收过程中,首先要对对象区域中的垃圾做标记,标记完成之后进入垃圾清理阶段,副垃圾回收器会把这些存活的对象复制到空闲区域中,同时还会把这些对象有序的排列起来,也就完成了内存整理的操作,完成复制之后空闲区域和对象区域进行翻转,这样就完成了垃圾对象的回收操作。

V8引擎采用了对象晋升策略,经过两次垃圾回收还存活的对象会被移动到老生区中。

3. 主垃圾回收器

主垃圾回收器存放的对象的特点是:1.对象占用空间大;2.对象存活时间长。

也就是采用复制对象的策略会会花费比较多的时间,所以主垃圾回收器采用的策略是标记-清除(Mark-Sweep)标记-整理(Mark-Compact) 两个算法进行垃圾回收的。

1. 标记-清除算法(Mark-Sweep):

a. 标记阶段:

  1. 初始标记(Initial Marking):

垃圾回收器从根对象开始,标记所有能够直接或间接访问到的对象。这个阶段的目标是快速标记那些与根对象直接关联的对象。

  1. 并发标记(Concurrent Marking):

在初始标记完成后,V8 引擎启动后台线程进行并发标记。这个阶段并发地标记那些与根对象间接关联的对象,不阻塞主线程执行 JavaScript 代码。

  1. 重标记(Re-Marking):

在并发标记完成后,需要执行最后的重标记步骤,确保在并发标记期间发生变化的对象也被正确标记。这一步会导致短暂的停顿。

b. 清除阶段:

  1. 清除无标记对象(Clear Unmarked)

2. 标记-整理算法(Mark-Compact):

在标记-清除算法的基础上,标记-整理算法引入了整理阶段,以减小内存碎片。

a. 整理阶段:

  1. 整理(Compacting Live Objects):

3. 增量标记(Incremental Marking):

V8 引擎的老生代垃圾回收器还包括增量标记技术。增量标记将标记阶段分解成多个小步骤,允许在标记过程中与 JavaScript 应用交替执行。这降低了垃圾回收导致的停顿时间,提高了应用的响应性。

相关推荐
Мартин.3 小时前
[Meachines] [Easy] Sea WonderCMS-XSS-RCE+System Monitor 命令注入
前端·xss
昨天;明天。今天。4 小时前
案例-表白墙简单实现
前端·javascript·css
数云界4 小时前
如何在 DAX 中计算多个周期的移动平均线
java·服务器·前端
风清扬_jd4 小时前
Chromium 如何定义一个chrome.settingsPrivate接口给前端调用c++
前端·c++·chrome
安冬的码畜日常4 小时前
【玩转 JS 函数式编程_006】2.2 小试牛刀:用函数式编程(FP)实现事件只触发一次
开发语言·前端·javascript·函数式编程·tdd·fp·jasmine
ChinaDragonDreamer4 小时前
Vite:为什么选 Vite
前端
小御姐@stella4 小时前
Vue 之组件插槽Slot用法(组件间通信一种方式)
前端·javascript·vue.js
GISer_Jing4 小时前
【React】增量传输与渲染
前端·javascript·面试
GISer_Jing4 小时前
WebGL在低配置电脑的应用
javascript
eHackyd4 小时前
前端知识汇总(持续更新)
前端