跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
JavaScript大前端算法

JavaScript 垃圾回收原理与 V8 分代回收

JavaScript 自动管理内存,但理解垃圾回收(GC)机制对性能优化至关重要。V8 引擎采用分代回收策略,将对象分为新生代和老生代。新生代使用复制算法快速回收短生命周期对象,老生代使用标记清除和标记整理处理长生命周期对象。常见内存泄漏原因包括意外全局变量、未清除定时器及闭包引用。避免泄漏需及时清理引用、减少全局变量并合理使用闭包。

黑客发布于 2026/3/23更新于 2026/5/83.5K 浏览

在我们编写 JavaScript 代码时,很少手动释放内存。不像 C/C++ 需要使用 free(),JS 引擎会帮我们自动回收内存。

但是,自动回收不等于不需要理解内存。很多前端的性能问题、页面卡顿、内存暴涨,其实都和垃圾回收机制有关。因此理解垃圾回收机制也尤为重要。

什么是垃圾回收?

垃圾回收(Garbage Collection,简称 GC):

自动识别'无法再访问'的对象,并释放其占用的内存。

核心问题只有一个:如何判断一个对象'没用了'?

那么基于这个定义,没有被引用的对象,就是垃圾。比如:

let obj = { name: "Tom" }
obj = null

当 obj = null 后:

  • 原来的对象不再被引用
  • 成为垃圾
  • 等待回收

内存模型基础

JavaScript 的内存中主要可以分为两个部分:

  • 栈(Stack)——存放基本类型和引用地址
    • 基本类型(number、string、boolean、null、undefined、symbol、bigint)
    • 函数的引用地址
  • 堆(Heap)——存放对象
    • 对象
    • 数组
    • 函数
    • 闭包
    • DOM 引用

例如:

let obj = { name: "Tom" }
  • obj 存在栈中,因为这个是引用地址,{ name: "Tom" } 存在堆中。

垃圾回收核心原理

那么怎么知道对象是否是垃圾?

标记清除算法

JS 引擎中有标记清除算法用来判断是否是垃圾,整个过程就是从 root 出发,看看是否能够被访问到。

Root 包括:

  • 全局对象(浏览器中的 window,Node 中的 global)
  • 当前调用栈中的变量
  • 正在执行函数里的局部变量
  • 闭包中被引用的变量

那么整个算法过程就分为标记阶段与清除阶段。

  1. 标记阶段

假设我们目前有以下这些对象:

Root ├── A
│     └── B
└── C
      D
  • A 被 Root 引用
  • B 被 A 引用
  • C 被 Root 引用
  • D 没有人引用

整个过程就是先从 root 出发,先找到了 A 与 C,为 A 与 C 打上了'可达'标记。

继续向下遍历通过 A 找到了 B,给 B 打上了可达标记,整个过程就是一次深度优先搜索(DFS)或广度优先搜索(BFS)。

遍历结束后:

  • A、B、C 被标记为'可达'
  • D 没被访问到
    1. 清除阶段

    引擎会扫描整个堆内存。

    对于每一个对象:

    • 如果有'标记' → 保留
    • 如果没有标记 → 释放内存

    因此 D 将会被回收。

    我们知道 JS 是单线程的,那么在垃圾回收的时候,主线程暂停,会执行这个垃圾回收,那么如果频繁进入垃圾回收就会造成页面卡顿。

    V8 的分代回收机制

    在早期垃圾回收模型中,所有对象都被一视同仁地扫描和回收。但在实际运行中,V8 发现了一个非常重要的现象:

    绝大多数对象的生命周期都非常短。

    例如:

    function render() {
      let temp = { x: 1, y: 2 }
      return temp.x
    }
    

    在这个函数中,temp 只在函数执行期间存在。函数执行结束后,它就不再被引用,很快就会成为垃圾对象。

    但与此同时,也存在一些对象会贯穿整个应用生命周期:

    const config = { ... }
    

    这些对象几乎不会被销毁。

    如果所有对象都使用同一种回收策略:

    • 要么频繁扫描整个堆(性能浪费)
    • 要么回收不及时(内存膨胀)

    因此,V8 采用了 分代垃圾回收机制(Generational GC)。

    思想介绍

    分代回收机制,将内存分成了两类:

    • 新生代(Young Generation)
    • 老生代(Old Generation)

    核心思想就是新创建的对象大概率会很快死亡;存活较久的对象,大概率会继续存活。

    新生代

    首先,新创建的对象都先放在新生代,这个空间会比较小,大小通常比较小就几 MB,并且这个空间回收频率快,回收速度高。

    整个新生代又分成两个空间:

    • From Space
    • To Space

    具体新生代使用的是 复制算法(Copying Collection),也称为 Scavenge 算法。

    算法流程:

    From Space: [A][B][C][D]
    To Space: [空]
    

    其中:

    • A、C 还被引用
    • B、D 已经不可达

    当垃圾回收执行时:

    1️⃣ 停止 JS 执行

    2️⃣ 从 Root 出发标记存活对象(这里与标记清除有区别,这里是标记复制,但是标记的流程是一样的)

    3️⃣ 把'活着的对象'复制到 To Space

    变成如下形式:

    To Space: [A][C]
    

    4️⃣ 清空整个 From Space

    5️⃣ 交换两个空间角色

    然后等待下一次执行。

    老生代

    那么什么时候对象会进入老生代呢?

    进入老生代的条件是:

    • 在新生代经历多次垃圾回收仍然存活
    • 对象太大

    老生代的特点就是:

    • 存放生命周期较长的对象
    • 空间更大
    • 回收频率更低
    • 回收成本更高

    我们已经知道新生代的算法是复制算法,但是老生代使用的算法是:

    • Mark-Sweep(标记清除)
    • Mark-Compact(标记整理)

    标记清除我们已经介绍过了,那么这里我们介绍标记整理。

    在清除后,整个内存空间会存在很多空隙碎片,那么就需要进行整理:

    [A][ ][B][ ][ ][C] |转换成下面的形式 [A][B][C][ ][ ][ ]
    

    对象会被'挪动'到连续空间。

    整个过程

    我们可以看下面这个流程:

    Root ↓ 新对象 → 新生代(复制算法) ↓(存活多次)晋升 ↓ 老生代(标记清除 + 标记整理)
    

    以上是整个分代回收基本流程。

    内存泄漏是什么

    内存泄漏就是该回收的对象一直被引用,导致无法回收。

    具体有:

    1️⃣ 意外的全局变量
    function test() {
      a = 10 // 忘记 var / let
    }
    
    2️⃣ 定时器未清除
    setInterval(() => {}, 1000)
    
    3️⃣ 闭包持有大对象
    function bibao() {
      let data = ...
      return function() {
        console.log(data)
      }
    }
    const test = bibao()
    // 当 test 不再被需要,仍然保持着对 data 引用,导致内存泄漏
    

    如何减少或避免内存泄漏

    为了避免内存问题,我们就需要针对上面可能出现的问题进行针对性解决。

    比如:

    • 少创建全局变量
    • 用完及时清除引用
    • 清除定时器
    • 避免不必要的闭包
    • 避免缓存巨大对象

    总结

    JavaScript 在 V8 中采用基于可达性的分代垃圾回收机制,通过复制算法优化短生命周期对象,通过标记清除与整理管理长生命周期对象。

    此外,还可以了解写屏障和记忆集解决跨代引用问题。

    目录

    1. 什么是垃圾回收?
    2. 内存模型基础
    3. 垃圾回收核心原理
    4. 标记清除算法
    5. V8 的分代回收机制
    6. 思想介绍
    7. 新生代
    8. 老生代
    9. 整个过程
    10. 内存泄漏是什么
    11. 1️⃣ 意外的全局变量
    12. 2️⃣ 定时器未清除
    13. 3️⃣ 闭包持有大对象
    14. 如何减少或避免内存泄漏
    15. 总结
    • 💰 8折买阿里云服务器限时8折了解详情
    • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
    • 代充Chatgpt Plus/pro 帐号了解详情
    • 🤖 一键搭建Deepseek满血版了解详情
    • 一键打造专属AI 智能体了解详情
    极客日志微信公众号二维码

    微信扫一扫,关注极客日志

    微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

    更多推荐文章

    查看全部
    • Java版LeetCode热题100之跳跃游戏:贪心算法的完美应用
    • MySQL 内置函数实战指南:日期、字符串与数学运算
    • Z-Image-Turbo WebUI 本地部署指南:环境配置与启动流程
    • Windows 11 配置 CUDA 版 llama.cpp 实现 GGUF 模型本地聊天
    • MySQL TIMESTAMPDIFF 函数详解:精准计算日期时间差值
    • Agentic AI 是什么?与传统 AIGC 的核心区别
    • Zotero 插件接入 DeepSeek AI 实现文献智能分析配置指南
    • SpringAI 结合 Ollama 本地部署 Deepseek 实现对话机器人(二)
    • 字节跳动 AI 原生 IDE Trae 安装与使用指南
    • 2026 年高校 AIGC 检测政策汇总
    • MATLAB 2025a 发布:深色模式与 Copilot 助手详解
    • Modbus CRC16 算法规范与计算示例
    • 开源 RAG UI:Kotaemon 构建文档问答系统
    • LangChain 入门:Memory 记忆组件详解
    • VSCode C++ 静态分析误报修复:IntelliSense 配置最佳实践
    • 2026 年国家自然科学基金申请书 AI 使用声明撰写位置指南
    • C++ 实现红黑树及 STL map 底层原理
    • GitHub 免费账户存储空间限制详解
    • 鸿蒙端云一体化开发实践:前端构建全栈应用
    • JavaScript 面试技巧与常见问题解析

    相关免费在线工具

    • 加密/解密文本

      使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

    • Gemini 图片去水印

      基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

    • Keycode 信息

      查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

    • Escape 与 Native 编解码

      JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

    • JavaScript / HTML 格式化

      使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

    • JavaScript 压缩与混淆

      Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online