JS有完善的内存处理机制,因此以前咱们不用特别的去关注这块的实现。页面不快了,刷新一下就行了;浏览器卡顿,重启一下就OK。可是随着SPA和移动APP的流行,以及将来可能存在的PWA的实现,JS内存可能成为新的内存瓶颈。这也是写本文的初衷。
当咱们决定再也不使用某些内存时,因为错误的编码,未能使得GC(Gabbage Collection)正确的将这些内存回收的状况,就是内存泄漏。javascript
一个对象占用的内存分为直接占用内存(Shallow Size)和占用总内存(Retained Size)。html
直接占用内存:对象自己占用的内存。典型的JavaScript对象都会有保留内存用来描述这个对象和存储它的直接值。通常,只有数组和字符串会有明显的直接占用内存(Shallow Size)。但字符串和数组经常会在渲染器内存中存储主要数据部分,仅仅在JavaScript对象栈中暴露一个很小的包装对象。
占用总内存:直接占用内存和这个引用的依赖对象所占用的内存。
赋值和New操做都会涉及到内存的占用。java
Chrome V8的垃圾回收(GC)算法基于Generational Collection,内存被划分为两种,分别称为Young Generation(YG)和Old Generation(OG)。node
所谓Young和Old是根据他们占用的时间来划分的。内存在YG的分配和回收快而频繁,通常存在的时间很短,因此称为Young;而在OG中则慢而少发生,因此称为Old。
由于在V8中,YG的GC过程会阻塞程序,而OG的GC不会阻塞。因此一般状况下开发者更关心YG的细节。算法
YG又被平分为两部分空间,分别称为From和To。全部内存从To空间被分配出去,当To满时,开始触发GC,接下来细看一下。chrome
某时刻,To已经分A、B和C分配了内存,当前它剩下一小块内存未分配出去,而From全部的内存都空闲着。数组
此时,一个程序须要为D分配内存,但D须要的内存大小超出了To未分配的内存,以下图。此时,触发GC,页面中止执行。浏览器
接着From和To进行对换,即原来的To空间被标志为From,From被标志为To。而且把活的变量值(例如B)标志出来,而”垃圾“(例如AC)未被标志,它们将会被清掉。闭包
活的B会被复制到To空间,而「垃圾」AC则被回收,同时,D被分配到To空间,最后成下图的分布dom
至此,整个GC完成,此过程当中页面中止执行,因此要尽量的快。当YG中的值存活比较久时,它会被推向OG,OG的空间满时,触发OG内的GC,OG的GC时会触发YG的GC。
- 每次分配都使To的可用空间减少,程序又更接近GC
- YG的GC会阻塞程序,因此GC时间不宜太长10ms之内,由于16ms就会出现丢帧;GC不宜太频繁
- 某个值变成垃圾后,不会立马释放内存,只有在GC的时候所占内存才会被回收。
2.2 内容均来自参考文献
GC Root是内存的根结节,在浏览器中它是window,在NodeJS中则是global对象。
从GC Root开始遍历图,全部能到达的节点称为活节点,若是存在GC Root不能到达的节点,那么该节点称为“垃圾”,将会被回收,如图中灰色的节点。
至于根节点的回收,不受用户的控制。
由于没有彻底切断与根节点之间的路径,致使自动GC不会回收这部份内存,从而形成内存泄漏。
具体的缘由有:
var a, b; a.reference = b; b.reference = a;
a = "1234567"; 至关于 window.a = "1234567";
<div id="myDiv"> <input type="button" value="Click me" id="myBtn"> </div> <script type="text/javascript"> var btn = document.getElementById('myBtn'); btn.onclick = function () { document.getElementById('myDiv').innerHTML = 'Processing...'; /* 清除事件绑定 */ // btn.onclick = null; }; </script>
function bindEvent() { var obj = document.getElementById('xxx'); obj.onclick = function () { /** 空函数*/ }; /** delete this reference */ // obj = null; }
// b是a的子dom节点, a是body的子节点 var aElement = document.getElementById("a"); var bElement = document.getElementById("b"); document.body.removeChild(aElement); // aElement = null; // bElement = null;
更多的出如今nodejs中,例如:
while(1) { // do sth }
var arr = []; for (var i=0; i< 100000000000; i++) { var a = { 'desc': 'an object' } arr.push(a); }
本文描述了内存分配和泄漏的基本原理,并说起了平常常遇到的集中的泄漏缘由。在下一篇文章中,将阐述如何肯定内存泄漏,以及可使用的工具和方法。