js垃圾回收

JS垃圾回收

什么是垃圾回收?

自动将用不到的内存清理掉,避免浪费内存

什么是垃圾?

js 复制代码
let a = {name:"Tom"};
a = null;
//原来的{name:"Tom"}没人再用+没有变量指向它 -> 垃圾对象  

如何实现垃圾回收?

标记清除 (最核心)

如果一个对象是'不可达'的,那么它就是垃圾

第一阶段:标记(Mark)
  1. 垃圾回收器在运行时会给内存中所有的变量都打上一个初始标记(通常是 0)。
  2. 从"根对象"开始,递归地遍历所有引用。
  3. 凡是能被访问到的对象,都会被重新标记(比如改为 1),表示它们是"活跃的"或"可达的"。
  4. 遍历结束后,那些标记依然是 0 的对象,就是无法从根部触达的对象。
第二阶段:清除(Sweep)垃圾回收器对内存进行扫描。
  1. 销毁那些标记为 0 的对象,并回收它们所占用的空间。
  2. 最后,把所有存活对象的标记重新置为 0,等待下一轮回收。

引用计数

看一个对象被引用多少次,当引用次数为0就回收

但是!解决不了循环引用

js 复制代码
let a = {};
let b = {};
a.x = b;
b.x = a;
//互相引用,引用数不为0,但实际上没用 -> 内存泄漏

对比:原始类型的回收

原始类型不会像对象那样复杂回收:原始类型存在栈中,生命周期跟作用域绑定

js 复制代码
function fn() {
    let a = 10;
}

执行完:整个作用域直接销毁 -> a自动消失,不需要复杂GC,但是! 在js引擎内部仍然会统一由GC管理,只是回收成本极低,不需要可达性分析。

综上 :是否回收,不看类型,而是看有没有引用

分代垃圾回收

分代垃圾回收主要是针对堆中的对象设计的,把对象分为新生代和老生代,用不同策略回收,提高效率

新生代

特点:对象多,生命周期短。回收频繁

复制算法(From正在用 -> To空)

  1. 把活着的对象复制到To区
  2. 清空From区
  3. 交换空间(To区 → 新的From区)

对象经历多次GC还没死,就会进入老生代


老生代

对象少,生命周期长

标记清除、标记整理算法

  1. 标记阶段

    遍历所有存活对象,打上标记。

  2. 整理阶段

    存活对象往内存一端紧凑移动,更新引用地址,然后直接回收端外整片空间

好处 :无内存碎片

为什么频繁创建对象会卡顿?(GC卡顿)

原因:垃圾回收会暂停js执行(STW stop the world)

创建多 → 垃圾多 → GC 更频繁 → 卡顿更多

频繁创建对象会增加垃圾回收的频率,而垃圾回收过程中可能触发 Stop-The-World,暂停主线程执行,从而导致页面卡顿

现代引擎如何优化标记清除

虽然标记清除解决了循环引用问题,但它也有原生缺陷:

  1. 内存碎片化:清除对象后,空闲的内存空间是不连续的。如果此时需要分配一个大对象,可能会因为找不到足够大的连续空间而失败。
  2. 全停顿(Stop-the-World):在进行 GC 时,JavaScript 的执行必须暂停,这会导致页面卡顿(Jank)。

策略

  1. 标记整理

  2. 分代回收

  3. 增量标记:将原本漫长的"一次性标记"拆分成许多小步,每执行一小段 JS 代码,就执行一小步标记。这样将"大停顿"分散成许多"微停顿",让用户感觉不到卡顿。

  4. 三色标记法:配合增量标记使用,将对象分为:

    • 白色:未被访问,待回收
    • 灰色:已被访问,但其应用的子对象还未处理完
    • 黑色:已被访问且其引用的对象也以处理完,是安全的

    它是如何通过颜色实现"进度保存"的呢?

    1. 开始:所有对象都是白色。
    2. 标记根对象 :从根对象出发,把直接引用的对象涂成灰色,放入一个队列。
    3. 逐步扫描
      • 从队列取出一个灰色 对象,将其引用的子对象涂成灰色
      • 等这个对象所有的子对象都涂成灰色后,把当前对象涂成黑色
    4. 暂停与恢复
      • 暂停:因为有"灰色"这个中间状态,回收器知道哪些地方还没扫完。即使 JS 引擎暂停 GC 去执行业务代码,它只要记住"灰色对象队列"就行了。
      • 恢复:回来后,直接从灰色对象继续往下扫,不需要从头开始。

    结论:三色标记法让 GC 标记过程可以"走走停停",大大减少了单次停顿的时间。

相关推荐
@yanyu66618 小时前
07-引入element布局及spring boot完善后端
javascript·vue.js·spring boot
@大迁世界18 小时前
2026年React大洗牌:React Hooks 将迎来重大升级
前端·javascript·react.js·前端框架·ecmascript
风止何安啊19 小时前
为什么要有 TypeScript?让 JS 告别 “薛定谔的 Bug”
前端·javascript·面试
海天鹰19 小时前
SOC架构
javascript
前进的李工20 小时前
MySQL角色管理:权限控制全攻略
前端·javascript·数据库·mysql
NPE~20 小时前
[App逆向]环境搭建下篇 — — 逆向源码+hook实战
android·javascript·python·教程·逆向·hook·逆向分析
洒满阳光的庄园20 小时前
Electron 桌面端打包流程说明
前端·javascript·electron
子琦啊20 小时前
【算法复习】数组与双指针篇
javascript·算法
SuperEugene21 小时前
前端通用基础组件设计:按钮/输入框/弹窗,统一设计标准|组件化设计基础篇
前端·javascript·vue.js·架构
范什么特西21 小时前
web练习
java·前端·javascript