请解释Node.js中的“垃圾回收”机制。
Node.js基于V8引擎,其垃圾回收机制是自动内存管理的重要手段,旨在回收不再使用的内存。V8引擎的内存分为新生代和老生代,采用不同的垃圾回收算法。 新生代采用Scavenge算法,将空间分为使用区和空闲区,通过复制存活对象到空闲区并交换角色来回收内存,速度快但空间利用率低。 老生代采用标记清除和标记整理算法。标记清除先标记存活对象,再清除未标记对象,但会产生内存碎片;标记整理在标记清除基础上,将存活对象移动到一起,使内存连续。 垃圾回收会暂停程序执行,对性能有一定影响。Node.js通过优化算法和调整内存分配策略来减少这种影响。同时,开发者应避免频繁创建和销毁大量对象,合理管理内存,以提高应用性能。需要注意的是,垃圾回收不是实时的,而是在合适的时机触发。 2. 背景知识 (1)内存管理的重要性 在程序运行过程中,需要不断地分配内存来存储数据。如果不及时回收不再使用的内存,会导致内存占用不断增加,最终可能引发内存泄漏,使程序性能下降甚至崩溃。 (2)垃圾回收的基本概念 垃圾回收(Garbage Collection,GC)是一种自动内存管理机制,它会自动识别并回收不再被程序使用的内存空间,让开发者无需手动管理内存的分配和释放。 3. 解析 (1)Node.js内存结构 Node.js基于V8引擎,V8引擎的内存主要分为新生代(New Space)和老生代(Old Space)。 新生代:用于存放存活时间较短的对象,空间较小,采用Scavenge算法进行垃圾回收。 老生代:用于存放存活时间较长的对象,空间较大,采用标记清除(Mark-Sweep)和标记整理(Mark-Compact)算法。 (2)Scavenge算法(用于新生代) 原理:将新生代空间划分为两个相等的区域,一个是使用区(From Space),一个是空闲区(To Space)。新创建的对象会被分配到使用区,当使用区快满时,会触发Scavenge算法。算法会遍历使用区中的对象,将存活的对象复制到空闲区,然后清空使用区,最后交换使用区和空闲区的角色。 特点:速度快,但空间利用率低,因为只能使用一半的新生代空间。 (3)标记清除和标记整理算法(用于老生代) 标记清除(Mark-Sweep): 标记阶段:从根对象开始遍历所有可达对象,将它们标记为存活。 清除阶段:遍历整个老生代空间,将未标记的对象(即不再使用的对象)清除。 缺点:会产生内存碎片,导致后续大对象分配时可能因空间不连续而失败。 标记整理(Mark-Compact):在标记清除的基础上,标记完成后,将存活的对象向一端移动,使内存空间连续,然后清除边界以外的内存。 (4)垃圾回收对性能的影响 垃圾回收是一个相对耗时的操作,在进行垃圾回收时,会暂停程序的执行(Stop The World),这可能会影响应用的性能。 Node.js通过优化垃圾回收算法和调整内存分配策略,尽量减少垃圾回收对性能的影响。例如,合理设置新生代和老生代的大小,避免频繁的垃圾回收。 v8 引擎将内存分为了新生代和老生代。 新创建的对象或者只经历过一次的垃圾回收的对象被称为新生代。经历过多次垃圾回收的对象被称为老生代。新生的对象容易早死,老生代活得更久 新生代被分为 From 和 To 两个空间。To 一般是闲置的,当 From 空间满了的时候会执行 Scavenge 算法进行垃圾回收。 这个算法分为三步: 1.首先检查 From 空间的存活对象, 如果判断这个存活对象是否满足晋升到老生代的条件,满足条件则晋升到老生代,如果不满足条件则移动 To 空间。 2.如果对象不存活,则释放对象的空间。 3.最后将 From 空间和 To 空间角色进行交换 V8 新生代对象晋升到老生代有两个条件: 1.第一个是判断对象否已经经过一次 Scavenge 回收。 若经历过,则将该对象从 From 空间复制到老生代中; 若没有经历,则复制到 To 空间。 2.第二个是 To 空间的内存使用占比是否超过限制。 当对象从 From 空间复制到 To 空间时,若 To 空间使用超过 25%,则对象直接晋升到老生代中。 设置 25% 的原因主要是因为算法结束后,两个空间结束后会交换位置,如果 To 空间的内存太小,会影响后续的内存分配。 使用--max-old-space-size命令来扩充堆内存空间 扩大老生代的内存空间 执行 node --max-old-space-size=8096 .\look.js 5. 常见误区 (1)认为垃圾回收是实时的 误区:认为一旦对象不再被引用,垃圾回收器会立即回收其内存。 纠正:垃圾回收器会在合适的时机进行回收,不是实时的。例如,当内存达到一定阈值或经过一定时间间隔时才会触发。 (2)忽视垃圾回收对性能的影响 误区:只关注程序的功能实现,不考虑垃圾回收对性能的影响。 纠正:在编写Node.js应用时,要注意避免频繁创建和销毁大量对象,合理管理内存,减少垃圾回收的频率。 (3)对不同算法的适用场景理解不清 误区:不清楚Scavenge、标记清除和标记整理算法的适用场景。 纠正:Scavenge算法适用于新生代,处理存活时间短的对象;标记清除和标记整理算法适用于老生代,处理存活时间长的对象。
来源整理自:vue3js.cn 面试官系列



