JavaScript GC简书

1.垃圾回收算法

垃圾:没法再被访问的对象或内存空间
延迟:指平均每次垃圾回收开始到结束须要的时间。
吞吐量:指平均必定时间内能回收多少内存,内存多少这个概念很是普遍,能够指多少个对象,也能够指多少字节的空间,具体的应该看指标应需求而异。
根节点:如全局变量上的对对象的引用、栈上对对象的引用等用户必定可以访问到的地址,是寻找活对象的入口。javascript

下面简单地介绍引用计数、Mark-Sweep、Mark-Copy、Mark-Compact四种垃圾回收算法java

1.1 引用计数node

这是最初级的垃圾收集算法。此算法把“对象是否再也不须要”简化定义为“对象有没有其余对象引用到它”。若是没有引用指向该对象(零引用),对象将被垃圾回收机制回收。咱们能够为每一个对象都增长一个计数器,来记录对这个对象的引用数量,当引用计数归零时,这个对象变成了垃圾算法

引用计数的优势以下:缓存

(1)内存释放及时,当一个对象死亡时其占用的内存立刻被释放
(2)延迟低,内存释放的时间均匀地分布在各个时间段

缺点以下:闭包

(1)每一个对象须要附带一个计数字段的空间
(2)引用复制和销毁时须要对改变计数字段,这可能涉及到相对昂贵的原子操做
(3)没法处理循环引用,好比两个对象互相引用对方的状况

进阶话题:当一个大对象的引用归零时,经常会致使一大批的对象引用归零,这种成批释放的状况很是常见,会致使垃圾回收的延迟上升以及可能占用大量栈空间去递归释放循环引用能够经过一些算法检测到,也能够在适当时刻使用其余垃圾回收算法来释放引用计数器的更新是存在冗余的,即一大部分的引用计数的更新是能够被消除的dom

1.2 标记清除 Mark-Sweep性能

除去引用计数,Mark-Sweep是另外一个思考方向。它分为**标记和清除**两个阶段。当垃圾回收被触发时,运行时从有限的根节点(在Javascript里,根是全局对象)出发,对全部可以到达的对象进行标记(通常为深度优先搜索),而后再遍历整个堆,清除全部未被标记的对象。spa

通常认为其优势有:code

(1)相比引用计数很难处理循环引用,Mark-Sweep算法总能找到全部没法被引用的对象
(2)因为垃圾总被一块儿批量回收,可能能够提升内存回收的吞吐
(3)这个算法实现起来简单

缺点以下:

(1)每一个对象须要附带至少一个比特做为标记的空间
(2)因为Mark阶段须要在整个堆上随机遍历,对CPU缓存不友好
(3)算法的性能与堆的大小相关,当堆很是大,而单次回收对象数量有限时,性能被严重拖累
(4)垃圾回收的延迟较高,会使用户代码彻底中止一段时间
(5)出现内存不连续的状态

进阶话题:增量标记,即经过对算法必定的修改,Mark阶段能够与用户程序交替执行直到标记阶段完成,以减小垃圾回收算法的延迟。

1.3 标记复制 Mark-Copy

Mark-Copy将堆内存一分为二,一个处于使用状态,一个处于闲置状态。当开始垃圾回收时,会检查使用状态的内存块,把存活的对象复制到闲置状态的内存块,完成复制后,两个内存空间交换角色。

相较于Mark-Sweep,其优势有:

(1)在回收垃圾的同时也整理内存,避免了内存碎片化的问题
(2)非侵入式的算法,不须要对象上的字段(理想是美好的,但现实每每不是)
(3)算法的执行时间仅与活对象的数量有关,不须要扫描整个堆
(4)分配对象时不须要寻找空闲空间,由于其总在当前使用的堆的末尾

缺点以下:

(1)回收时须要进行大量的内存拷贝
(2)内存利用率低,维护了两个堆,却只用了一半的空间

进阶话题:
经过分块的方式维护N个堆,以提升内存利用率
对活对象进行分代维护

1.4 标记整理 Mark-Compact

注意Mark-Copy算法须要维护一个额外的堆来做为拷贝活对象的容器。标记整理和标记清除的差异在于对象标记死亡后,在整理内存的过程当中,将活着的对象往一端移动,移动完成后,直接清理边界外的内存。

能够说Mark-Compact是Mark-Copy和Mark-Sweep算法的一种整合,其优缺点也只是前两种算法各取部分。

标记清除,标记复制,标记整理特色

(1)标记清除只复制活着的对象,用空间换取时间,速度最快
(2)标记复制只清除死亡的对象
(3)标记整理是二者的整合,速度最慢


2.V8 内存管理和垃圾回收机制

不一样的引擎有不一样的GC实现方式。这里就介绍V8 内存管理和垃圾回收机制

新生代和老生代

V8 将内存分为两类:新生代内存空间和老生代内存空间,新生代内存空间主要用来存放存活时间较短的对象,老生代内存空间主要用来存放存活时间较长的对象。对于垃圾回收,新生代和老生代有各自不一样的策略。

 

new_old_generation.jpg

新生代主要使用Scavenge垃圾回收算法进行管理,主要实现是Cheney算法,将内存平均分为两块,使用空间叫From,闲置空间叫To,新对象都先分配到From空间中,在空间快要占满时将存活对象复制到To空间中,而后清空From的内存空间,此时,调换From空间和To空间,继续进行内存分配,当知足那两个条件时对象会重新生代晋升到老生代。也就是上面提到的标记复制式的算法

老生代主要采用Mark-Sweep和Mark-Compact算法,一个是标记清除,一个是标记整理。二者不一样的地方是,Mark-Sweep在垃圾回收后会产生碎片内存,而Mark-Compact在清除前会进行一步整理,将存活对象向一侧移动,随后清空边界的另外一侧内存,这样空闲的内存都是连续的,可是带来的问题就是速度会慢一些。在V8中,老生代是Mark-Sweep和Mark-Compact二者共同进行管理的。因为Mark-Conpact须要移动对象,因此它的执行速度不可能很快,在取舍上,V8主要使用Mark-Sweep,在空间不足以对重新生代中晋升过来的对象进行分配时,才使用Mark-Compact。


3.javascript 内存泄露

3.1 全局变量引发的内存泄漏

3.2 闭包引发的内存泄漏

3.3 dom清空或删除时,事件未清除致使的内存泄漏

3.4 子元素存在引用引发的内存泄漏

 

detached-nodes.gif

  • 黄色是指直接被 js变量所引用,在内存里
  • 红色是指间接被 js变量所引用,如上图,refB 被 refA 间接引用,致使即便 refB 变量被清空,也是不会被回收的
  • 子元素 refB 因为 parentNode 的间接引用,只要它不被删除,它全部的父元素(图中红色部分)都不会被删除

做者:echozzh
连接:https://www.jianshu.com/p/18532079bc2a 来源:简书 著做权归做者全部。商业转载请联系做者得到受权,非商业转载请注明出处。

相关文章
相关标签/搜索