1.垃圾回收(Garbage collection)
a.对象创建
初始化赋值: var n = 123; // allocates memory for a number var s = 'abc'; var a = [1, null, 'aaa']; 实例化对象 var d = new Date();
b.javascript垃圾回收,内存出现泄漏一般都发生在回收阶段.
低级语言,需要手动管理内存的分配和释放,javascript作为一种高级语言,垃圾回收器,当对象创建时会自动分配内存,当对象不再被使用的时候会自动释放内存。 如果一个对象不再被引用,就会被GC回收。
http://newhtml.net/v8-garbage-collection/
Mark-and-sweep 算法:
此算法中,会有roots,Garbage-collector 会定期的从这些roots开始查找,查找所有被roots引用的对象或者被这些对象引用的对象(对象被roots引用或者被其它对象引用,就表示还在使用),所有不可到达的对象(没有找到),会被GC回收。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management
内存分代:
脚本中,绝大多数对象的生存期很短,只有某些对象的生存期较长。为利用这一特点,V8将堆进行了分代。对象起初会被分配在新生区(通常很小,只有1-8 MB,具体根据行为来进行启发)。在新生区的内存分配非常容易:我们只需保有一个指向内存区的指针,不断根据新对象的大小对其进行递增即可。当该指针达到了新生区的末尾,就会有一次清理(小周期),清理掉新生区中不活跃的死对象。对于活跃超过2个小周期的对象,则需将其移动至老生区。老生区在标记-清除或标记-紧缩(大周期)的过程中进行回收。大周期进行的并不频繁。一次大周期通常是在移动足够多的对象至老生区后才会发生。至于足够多到底是多少,则根据老生区自身的大小和程序的动向来定。
2.如何使用诊断工具发现性能问题
a.任务管理器
a.timeline
释放过之后
b.Take Heap Snapshot:页面中JavaScript对象和dom节点的内存分配。
Summary
Comparison
Containment
Statistics
c.Record Javascript CPU Profile:记录每个函数的执行时间,Self Time,Total Time
d.Record Allocation Timeline:记录堆上构造函数的内存分配,每个构造函数对应的实例数量,浅层大小,保留大小
e.Record Allocation Profile:记录每个函数的内存使用大小。
举个例子:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8" /> <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0;" name="viewport" > <title>index page</title> </head> <body> <input type="button" value="buttoon" id="btn" /> <script type="text/javascript"> // var btn=document.getElementById("btn"); // btn.addEventListener("click",function(){ var ClassA = function(name){ this.name = name; this.func = null; }; var a = new ClassA("a"); var b = new ClassA("b"); b.func = bind(function(){ console.log("I am " + this.name); }, a); b.func(); //输出 I am a //a = null; //释放a //b = null; //释放self //模拟上下文绑定 function bind(func, self){ return function(){ return func.apply(self); }; }; // },false); </script> </body> </html>
由于,a和b都没有释放,可以看到classA下面有两个实例。a 实例的引用关系可以在Retainers 窗口中看到。a是通过self这个形参传入的,返回是个函数,又被func引用,func又是b的属性。
释放a之后的内存:a虽然已经是null,但是a仍然被b引用,所以不会释放。
正确做法:a和b都是null,才会释放。
3.常见的性能问题
a.局部变量意外变成全局变量
b.闭包,用过没有释放
c.setTimeout 没有释放
d.事件绑定之后,不再需要时,没有移除
学习资料:
profile使用:
https://www.2cto.com/kf/201402/281855.html
https://blog.csdn.net/taoerchun/article/details/51480949
原理及总体分析:
http://www.ayqy.net/blog/js%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F%E6%8E%92%E6%9F%A5%E6%96%B9%E6%B3%95/
内存泄漏排查步骤:
1.timeline 查看实时内存,是否不断上升(手动GC)。如果不断上升,说明可能存在内存泄露。
2.记录开始对快照,隔段时间的对快照,compare一下,找出内存泄漏的点。
3.如果明确知道某些操作会导致卡顿或者页面挂掉,可以在操作前后各记录一次对快照,进行对比。找出内存泄漏点。