javascript性能优化

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>
View Code

由于,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.如果明确知道某些操作会导致卡顿或者页面挂掉,可以在操作前后各记录一次对快照,进行对比。找出内存泄漏点。

原文地址:https://www.cnblogs.com/bg57/p/6760512.html