深度理解js的垃圾回收机制


#【垃圾回收

#概念

我们平时创建所有的数据类型都需要内存,所谓的垃圾回收就是找出那些不再继续使用的变量,然后释放出其所占用的内存,垃圾回收会按照固定的时间间隔周期性的执行这一操作。

JavaScript具有自动垃圾回收机制,它负责在代码执行过程中自动释放不再使用的内存空间,以避免内存泄漏和资源浪费。

 javaScript 使用的垃圾回收机制来自动管理内存,垃圾回收是把双刃剑;垃圾回收是不可见的

  • 优势:可以大幅简化程序的内存管理代码,降低程序员的负担,减少因长时间运转而带来的内存泄漏问题。

  • 不足:程序员无法掌控内存,javascript 没有暴露任何关于内存的 api,无法强迫进行垃圾回收,无法干预内存管理。


#垃圾回收的方式

引用计数(reference counting)

跟踪记录每个值被引用的次数,如果一个值引用次数是 0,就表示这个值不再用到了,因此可以将这块内存释放

原理:每次引用加 1,被释放减 1,当这个值的引用次数变成 0 时,就将其内存空间释放。


标记清除(现代浏览采用标记清除的方式)

标记清除指的是当变量进入环境时,这个变量标记为“进入环境”;而当变量离开环境时,则将其标记为“离开环境”,最后垃圾收集器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间(所谓的环境就是执行环境)。

什么是全局环境?

可以理解最外围的执行环境,根据据宿主环境的不同表示的执行环境的对象也不一样,在浏览器中全局执行环境被认为是 window 对象。全局变量函数都是作为 window 对象的属性和方法创建的。某个执行环境中的所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁(全局执行环境只有当关闭网页的时候才会被销毁)。

什么是局部环境?

每个函数都有自己的执行环境,当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境,ECMAScript 程序中的执行流正是由这个方便的机制控制着。

    function foo (){
       var a = 10   // 被标记进入执行环境
       var b = ‘hello’ // 被标记进入执行环境
    }
    foo()  //执行完毕,a 和 b 被标记离开执行环境,内存被回收


#【V8 内存管理机制】

#V8 引擎限制概念

现代浏览器通常使用V8引擎。V8是一款由Google开发的高性能JavaScript引擎,用于解释和执行JavaScript代码。V8引擎最初为Google Chrome浏览器而开发,后来也被许多其他浏览器采用,包括Opera、Microsoft Edge(Chromium版)和部分Safari等。

除了V8引擎,还有其他的JavaScript引擎可用于浏览器,如SpiderMonkey(Mozilla Firefox浏览器)、JavaScriptCore(原生Safari浏览器)和Chakra(Microsoft Edge旧版)。


#V8 引擎限制内存的原因

  • V8 最初为浏览器设计,不太可能遇到大量内存的使用场景(表层原因)

  • 防止因为垃圾回收所导致的线程暂停执行的时间过长(深层原因,按照官方的说法以 1.5G 的垃圾回收为例,v8 做一次小的垃圾回收需要 50 毫秒以上,做一次非增量的垃圾回收需要 1 秒以上,这里的时间是指 javascript 线程暂停执行的时间,这是不可接受的, v8 直接限制了内存的大小,如果说在 node.js 中操作大内存的对象,可以通过去修改设置去完成,或者是避开这种限制,1.7g 是在 v8 引擎方面做的限制,我们可以使用 buffer 对象,而 buffer 对象的内存分配是在 c++层面进行的,c++的内存不受 v8 的限制)

#V8 回收策略

  • v8 采用可一种分代回收的策略,将内存分为两个生代;新生代老生代

  • v8 分别对新生代和老生代使用不同的回收算法来提升垃圾回收效率




新生代的回收方法

新生代内存被分为两个相等大小的空间,一半是From空间,一半是To空间。当进行垃圾回收时,V8引擎将活动对象从From空间复制到To空间,同时对活动对象进行标记,然后清除From空间中的所有对象。这个过程被称为"Scavenge",目的是清除不再使用的对象并将存活对象移到To空间。在下一次垃圾回收时,From空间和To空间互换角色,重复上述过程。


 新生代对象的晋升(新生代中用来存放,生命较短的对象,老生代存放生命较长的对象)

就是说新生代会晋升到老生代。

老生代垃圾回收(有 2 种回收方法"标记-清除-整理"(Mark-Sweep-Compact)的回收算法

标记清除(Mark Sweep) Mark Sweep 是将需要被回收的对象进行标记,在垃圾回收运行时直接释放相应的地址空间,红色的区域就是需要被回收的

标记合并(Mark Compact) Mark Compact 将存活的对象移动到一边,将需要被回收的对象移动到另一边,然后对需要被回收的对象区域进行整体的垃圾回收


通过将堆内存划分为新生代和老生代,V8引擎可以根据对象的存活时间采用不同的回收策略和算法,以提高垃圾回收的效率和性能。分代回收是V8引擎中的重要优化策略之一,可以更精细地管理对象的回收,从而提高整体的性能。


by the way,说到垃圾回收,就得说一嘴js闭包,闭包是在函数执行完毕后仍然保持对其词法环境的引用,这使得内部函数可以继续访问操作外部函数的变量,而不会被垃圾回收机制回收。

声明:BenBonBen博客|版权所有,违者必究|如未注明,均为原创

转载:转载请注明原文链接 - 深度理解js的垃圾回收机制


过去太迟,未来太远,当下最好