现代 JavaScript 引擎垃圾回收机制以可达性判断为基础,结合分代处理与空间压缩协同工作:标记阶段从根对象出发遍历引用链标记活跃对象,解决循环引用问题;压缩阶段将存活对象集中迁移并更新引用,消除内存碎片、提升分配效率与GC性能。

现代 JavaScript 引擎(如 V8)的垃圾回收不是简单“删掉不用的对象”,而是围绕可达性判断 + 分代处理 + 空间整理构建的一套协同机制。标记与空间压缩是其中关键环节,它们共同解决两个核心问题:准确识别垃圾、高效复用内存。
标记阶段:从根出发,精准锁定活跃对象
标记不是逐个检查变量名,而是从一组“根对象”出发,顺着引用链遍历所有能到达的对象:
- 根对象包括:全局对象(
window或global)、当前调用栈中的局部变量和参数、DOM 根节点、正在运行的定时器回调中捕获的变量等 - 只要一个对象能通过任意一条引用路径从根抵达,它就被标记为“活跃”,不会被回收
- 这天然解决了循环引用问题——即使
a持有b、b持有a,只要它们整体不可达,整个闭环都会被清除
空间压缩:不只是清理,更是整理内存布局
单纯清除未标记对象会产生大量内存碎片,导致后续分配大对象时频繁触发 GC。压缩(Compaction)把所有存活对象集中搬移到堆内存一端,腾出连续的大块空闲空间:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- V8 在老生代 GC 中默认启用 Mark-Sweep-Compact 流程:标记 → 清扫 → 移动并更新引用
- 移动过程中,引擎会同步修正所有指向这些对象的指针(比如变量里的引用地址),保证程序逻辑不变
- 压缩后内存更紧凑,分配新对象更快,也减少了因碎片导致的额外 GC 压力
为什么需要压缩?不只为了省空间
压缩的价值远超“释放更多字节”:
立即学习“Java免费学习笔记(深入)”;
- 避免“假性内存不足”:碎片化严重时,即使总空闲内存足够,也可能无法分配一个 1MB 的数组
- 提升后续分配效率:连续内存支持快速 bump-pointer 分配,比在碎片中查找空闲块快得多
- 降低 GC 频次:压缩后空间利用率高,新生代晋升到老生代的对象更少被“挤”进老生代,间接减少昂贵的老生代 GC 触发
实际开发中可感知的优化点
压缩本身由引擎自动完成,但开发者行为会影响其效果:
- 避免长期持有临时大对象引用(如缓存未及时清理的图片数据、日志数组),否则它们会被晋升到老生代,增加压缩开销
- 批量创建对象时尽量复用结构(如使用对象池或固定字段的类),减少形状变化带来的隐藏类分裂,间接利于压缩时的内存对齐
- 移除 DOM 元素前,手动解绑事件监听器、清除定时器,确保相关闭包和数据能被完整标记为不可达

















