JavaScript GC
1.垃圾回收算法
垃圾:无法再被访问的对象或者内存空间
推迟:指平均每次垃圾回收开始到结束需要的时间。
吞吐量:指平均肯定时间内能回收多少内存,内存多少这个概念非常广泛,可以指多少个对象,也可以指多少字节的空间,具体的应该看指标应需求而异。
根节点:如全局变量上的对对象的引用、栈上对对象的引用等客户肯定能够访问到的地址,是寻觅活对象的入口。
下面简单地详情引用计数、Mark-Sweep、Mark-Copy、Mark-Compact四种垃圾回收算法
1.1 引用计数
这是最初级的垃圾收集算法。此算法把“对象能否不再需要”简化定义为“对象有没有其余对象引用到它”。假如没有引用指向该对象(零引用),对象将被垃圾回收机制回收。我们可以为每个对象都添加一个计数器,来记录对这个对象的引用数量,当引用计数归零时,这个对象变成了垃圾
引用计数的优点如下:
(1)内存释放及时,当一个对象死亡时其占用的内存马上被释放(2)推迟低,内存释放的时间均匀地分布在各个时间段缺点如下:
(1)每个对象需要附带一个计数字段的空间(2)引用复制和销毁时需要对改变计数字段,这可能涉及到相对昂贵的原子操作(3)无法解决循环引用,比方两个对象互相引用对方的情况进阶话题:当一个大对象的引用归零时,常常会导致一大批的对象引用归零,这种成批释放的情况非常常见,会导致垃圾回收的推迟上升以及可能占用大量栈空间去递归释放循环引用可以通过少量算法检测到,也可以在适当时刻使用其余垃圾回收算法来释放引用计数器的升级是存在冗余的,即一大部分的引用计数的升级是可以被消除的
1.2 标记清理 Mark-Sweep
除去引用计数,Mark-Sweep是另一个思考方向。它分为标记和清理两个阶段。当垃圾回收被触发时,运行时从有限的根节点(在Javascript里,根是全局对象)出发,对所有能够到达的对象进行标记(一般为深度优先搜索),而后再遍历整个堆,清理所有未被标记的对象。
一般认为其优点有:
(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 的间接引用,只需它不被删除,它所有的父元素(图中红色部分)都不会被删除
参考
深入浅出nodeJs p111-119
聊聊V8引擎的垃圾回收
JavaScript常见的内存泄漏起因
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » JavaScript GC