
文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载JavaScript 的自动内存管理对开发者完全透明我们创建基本类型、对象、函数它们占据内存当不再需要时由引擎自动回收。本指南以《Modern JavaScript Tutorial》的 Garbage Collection 章节1-js/04-object-basics/03-garbage-collection/article.md为核心骨架系统讲解可达性Reachability这一内存管理核心概念、从根到对象引用链的判定规则、经典的 mark-and-sweep 标记清除算法以及现代引擎如 V8的分代收集、增量收集、空闲期收集等优化策略。学完后你将理解对象何时被保留、何时被回收能解释互相引用的对象依然可以被整体回收这类内存现象并为后续学习 WeakMap/WeakSet、WeakRef 等弱引用数据结构打下扎实基础。内存管理的核心概念可达性JavaScript 引擎中的内存管理是自动且隐式的。开发者创建基本类型primitives、对象objects、函数functions它们都会占用内存。问题随之而来当一个值不再被需要时引擎如何发现它并清理它回答这个问题的关键概念就是可达性Reachability。简单来说可达的值就是那些能够被访问或使用的值它们被保证存储在内存中不会被回收。判定规则分为两层根基Roots存在一组天生可达、出于显而易见的原因不能被删除的值例如当前正在执行的函数以及它的局部变量和参数当前嵌套调用链上的其他函数以及它们的局部变量和参数全局变量还有一些其他的内部值如引擎内部使用的引用。从根可达其他任何值只要它能通过一个引用或一串引用链从某个根到达就被认为是可达的。例如如果全局变量中有一个对象而该对象的一个属性又引用了另一个对象那么那个对象也是可达的它引用的对象同样可达。在 JavaScript 引擎内部有一个运行在后台的进程即垃圾回收器Garbage Collector。它监视所有对象并移除那些已经变得不可达的对象。最简单的例子丢失引用即被回收这是最简单的示例// user 持有对该对象的引用 let user { name: John };图中的箭头表示一个对象引用全局变量user引用了对象{name: John}我们简称为 John。John 的name属性存储的是一个基本类型值所以它被画在对象内部。如果user的值被覆盖引用就丢失了user null;此时 John 变得不可达没有任何方式可以访问它也没有任何引用指向它。垃圾回收器会把这份数据当作垃圾清除释放其占用的内存。这里需要提醒一个前置认知对象与基本类型的本质区别在于对象是按引用存储和拷贝的。正如 1-js/04-object-basics/02-object-copy/article.md 中所讲变量保存的并非对象本身而是其在内存中的地址引用。正是这种引用模型决定了垃圾回收的判定是基于从根出发的引用链这也是本章一切讨论的前提。两个引用只要还有一个引用就存活现在假设我们把引用从user复制给了admin// user 持有对该对象的引用 let user { name: John }; let admin user; // 复制引用而非复制对象此时如果我们执行同样的操作user null;……对象依然可以通过admin这个全局变量被访问因此它必须继续留在内存中。只有当admin也被覆盖例如admin null之后这个对象失去了所有引用才会被垃圾回收器移除。互相引用的对象引用不等于可达来看一个更复杂的例子——家庭结构function marry(man, woman) { woman.husband man; man.wife woman; return { father: man, mother: woman } } let family marry({ name: John }, { name: Ann });函数marry通过让两个对象互相持有对方的引用来撮合它们并返回一个同时包含两者的新对象。最终的内存结构如下目前所有对象都是可达的。现在移除两个引用delete family.father; delete family.mother.husband;注意只删除这两个引用中的一个是不够的因为那样所有对象仍然可达。但如果两者都删除我们可以看到 John 不再有任何传入引用incoming references传出引用outgoing references无关紧要只有传入引用才能让对象保持可达。因此 John 现在不可达了它连同所有随之变得不可访问的数据一起会被从内存中移除。垃圾回收之后不可达孤岛整片互相引用的对象被整体回收可能出现这样的情况一整片互相链接的对象组成的孤岛整体变得不可达并被从内存中移除。沿用上面的源码然后执行family null;内存图景变成这个例子充分展示了可达性概念的重要性。显而易见John 和 Ann 仍然互相链接彼此都有传入引用但这不够。原先的family对象已经从根上被切断没有任何引用指向它于是整片孤岛都变得不可达将被整体移除。这一结论在实际项目中非常关键两个对象互相引用并不能阻止它们被回收。例如闭包、事件监听器、缓存对象之间形成环状引用时只要没有从根可达的引用链环照样会被回收。这也是 1-js/06-advanced-functions/03-closure/article.md 中讨论闭包内存语义的基础。内部算法标记清除Mark-and-Sweep基础的垃圾回收算法被称为标记清除mark-and-sweep。垃圾回收器会定期执行以下步骤垃圾回收器取出所有根roots并标记记住它们然后访问并标记从根出发的所有引用接着访问这些被标记的对象并标记它们的引用。所有被访问过的对象都会被记下来以避免将来重复访问同一个对象……如此继续直到从根出发的所有可达引用都被访问完毕最后除被标记对象之外的所有对象都被移除。例如假设对象结构如下可以清晰地看到右侧有一个不可达孤岛。下面看标记清除垃圾回收器如何处理它。第一步标记根第二步顺着根的引用标记被引用的对象第三步只要还有可能就继续顺着引用往下标记最后在遍历过程中无法被访问到的对象被认为不可达将被移除我们也可以把整个过程想象成从根上泼下一大桶油漆油漆顺着所有引用流淌把所有可达对象都染上颜色未被染色的对象随后被清除。这就是垃圾回收工作的基本概念。现代引擎的优化策略标记清除是概念基础而各 JavaScript 引擎在其之上应用了大量优化以让它运行得更快、避免在代码执行中引入延迟。常见的优化包括分代收集Generational collection对象被划分为新生代与老年代两组。在典型代码中许多对象寿命极短出现、干完活、很快消亡。因此集中追踪新生对象并清理它们是划算的而那些存活足够久的对象会晋升为老年代被检查的频率更低。增量收集Incremental collection如果对象数量庞大一次性遍历并标记整个对象集合可能耗时较长造成可见的执行延迟。因此引擎把现有对象集合拆成多个部分一部分一部分地清理用多次小型垃圾回收替代一次总回收。这需要在各次回收之间做一些额外的簿记bookkeeping来追踪变化但换来的是一连串微小延迟而非一次大停顿。空闲期收集Idle-time collection垃圾回收器只在 CPU 空闲时运行以尽量降低对执行的影响。此外还存在其他优化思路与垃圾回收算法的变体。不同引擎实现着不同的技巧与手法而且随着引擎的持续演进这些技术也在不断变化——在尚无实际需要时过早地深入研究底层细节未必划算。如果确实感兴趣可以进一步阅读 V8 博客关于内存管理变化的文章或参考《The Garbage Collection Handbook: The Art of Automatic Memory Management》(R. Jones 等) 这类专著。可达性概念在仓库后续章节中的延伸可达性原则并不仅限于本章它是整个教程中理解若干高级主题的基石WeakMap 与 WeakSet1-js/05-data-types/08-weakmap-weakset/article.md普通Map会强引用键对象——只要 Map 存在作为键的对象就存在并占据内存而WeakMap不会阻止键对象被垃圾回收。当键对象失去所有其他引用后它会自动从内存以及 Map 中被移除因此WeakMap不支持迭代与keys()/values()/entries()方法——引擎可能在任意时刻清空其中的键当前键的数量在技术上不可知。WeakRef 与 FinalizationRegistry1-js/99-js-misc/07-weakref-finalizationregistry/article.md普通引用是强引用只要存在强引用对象就不会被垃圾回收器删除WeakRef则持有弱引用不阻止其目标对象referent被回收。例如// user 变量持有对象的强引用 let user { name: John }; // 把强引用复制到 admin 变量 let admin user; // 覆盖 user 变量的值 user null; // 对象仍然通过 admin 变量可达不会被删除只有当对象不再存在任何强引用例如admin也被覆盖时它才会被从内存中删除。这些弱数据结构正是建立在本章可达性原理之上的工程实践。总结本章需要掌握的核心要点垃圾回收是自动执行的我们无法强制它发生也无法阻止它发生对象在可达期间一直保留在内存中被引用不等于可达从根出发正如上面的例子所示一整片互相引用的对象可以作为一个整体变得不可达现代引擎如 V8在基本标记清除算法之上实现了分代收集、增量收集、空闲期收集等高级算法。理解可达性有助于写出内存更可控的代码例如及时释放不再需要的全局引用、理解缓存与闭包的生命周期并在需要关联生命周期跟随对象的数据时正确选用 WeakMap/WeakSet。对引擎底层知识的深入掌握是在熟悉语言本身之后、需要进行底层性能优化时的下一步合理规划。赞分享文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载相关推荐Font Awesome workflow for Alfred提升效率的终极图标搜索工具Font Awesome workflow for Alfred提升效率的终极图标搜索工具 如果你是一名设计师或开发者经常需要在项目中使用 Font Awe开发工具harelba/q垃圾回收Node.js V8引擎优化实践harelba/q垃圾回收Node.js V8引擎优化实践 你是否在处理大型日志文件时遇到过 q 命令运行缓慢甚至内存溢出的问题当解析超过1GB的CSV数据数据分析开发工具Duktape垃圾回收机制终极指南引用计数与标记清除的完美结合 Duktape垃圾回收机制终极指南引用计数与标记清除的完美结合 Duktape是一个轻量级、可嵌入的JavaScript引擎专为嵌入式系统和资源受限环语言运行时嵌入式解释器上一篇沉浸式翻译跨浏览器翻译一文看懂切换 Chrome、Edge、Firefox 不再重新配置下一篇如何3分钟免费激活IDM永久解锁完整版下载加速功能创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考