菜单

澳门金沙国际:JavaScript内存泄漏(转)

2019年1月31日 - 金沙前端

Chrome开发者工具不完全指南(四、品质进阶篇)

2015/07/05 · HTML5 ·
Chrome

原稿出处:
卖烧烤夫斯基   

前言

Profiles面板功用的作用至关重若是监控网页中各个方式执行时间和内存的变化,简单的话它就是Timeline的数字化版本。它的作用选项卡不是诸多(只有三个),操作起来比较前边的几块成效版本的话简单,不过其中的数目确很多,很杂,要弄懂它们须求花费一些时日。更加是在内存快照中的各类庞杂的数额。在那篇博客中卤煮将继续给大家分享Chrome开发者工具的应用经验。假诺您遇到不懂的地点或者有不规则的地点,可以在评论中回复卤煮,小说最终卤煮会最终把秘籍交出来。上边要介绍的是Profiles。首先打开Profiles面板。

澳门金沙国际 1

Profiles界面分为左右七个区域,左侧区域是放文件的区域,左边是突显数据的区域。在上马检测从前可以看出左边区域有五个选项,它们分别代表者不相同的功用:

1.(Collect JavaScript CPU Profile)监控函数执行期开支的大运
2.(Take Heap Snapshot)为近期界面拍一个内存快照
3.(Record Heap Allocations)实时督查记录内存变化(对象分配跟踪)

一、Collect JavaScript CPU Profile(函数收集器)

首先来关注首先个成效,(Collect JavaScript CPU
Profile)监察函数执行期开支的时日。讲道理不如举例子,为了更明了地询问它的法力概况,大家得以编制一个测试列子来察看它们的功效。这一个列子不难一些,使得我们解析的数额更清晰一些。

XHTML

<!DOCTYPE html> <html> <head>
<title></title> </head> <body> <button
id=”btn”> click me</button> <script
type=”text/javascript”> function a() { console.log(‘hello world’); }
function b() { a(); } function c() { b(); }
document.getElementById(‘btn’).addEventListener(‘click’, c, true);
</script> </body> </html>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<button id="btn"> click me</button>
<script type="text/javascript">
function a() {
console.log(‘hello world’);
}
 
function b() {
a();
}
 
function c() {
b();
}
 
document.getElementById(‘btn’).addEventListener(‘click’, c, true);
</script>
</body>
</html>

在右侧区域中接纳Collect JavaScript CPU
Profile
 选项,点击下方的Start按钮(也得以点击右边的黑色圆圈),那时候Chrome会起初记录网页的章程执行,然后大家点击界面的按钮来推行函数。最终再点击右边区域的Stop按钮(或者左侧的灰色圆圈),那时监控就离世了。右边Profiles会列出一个文件,单击可以看看如下界面:

澳门金沙国际 2

活着了一个数额表格,它们的意义在上图中早就标记出来了。它记录的是函数执行的小时以及函数执行的次第。通过右侧区域的品种选用可以切换数据显示的法子。有正蕴涵关系,逆包涵关系,图表类型二种选项。大家得以挑选其中的图片类型:

澳门金沙国际 3

可以见见这么些面板似曾相识,没错,它跟以前的TimeLine面板很像,的确,即使很像,但功效不雷同,不然也就没必要重复做了。从上图可以见到点击按钮执行的相继函数执行的年华,顺序,包罗关系和CUP变化等。你可以在变化文书从此在左手区域中保存该文件记录,下次只需求在区域2那中点击load按钮便可以加载出来。也就是说你可以本地永久地记下该段时间内的法子执行时间。首个职能大概就那样多,相比其余五个来说不难。

二、Take Heap Snapshot(内存快照**

上面大家来介绍一下次之个职能的用法。第四个效益是给当下网页拍一个内存快照.选择第一个拍摄作用,按下 Take
Snapshot 按钮,给当下的网页拍下一个内存快照,获得如下图。

澳门金沙国际 4

可以看看左侧区域生成个文件,文件名下方有数字,表示这几个张快照记录到的内存大小(此时为3.2M)。左侧区域是个列表,它分成五列,表头可以根据数值大小手动排序。在那张表格中列出的部分列数字和标识,以及表头的含义相比较复杂,涉及到有些js和内存的学识,我们就先从这几个表头起始询问她们。从左到右的相继它们各自代表:
Constructor(构造函数)表示所有通过该构造函数生成的靶子
Distance 对象到达GC根的最短距离
Objects Count 对象的实例数
Shallow size 对应构造函数生成的靶子的shallow
sizes(直接占用内存)总数
Retained size 显示了对应对象所占用的最大内存
CG根!是神马东西?在google的官方文档中的指出是CG根不必用到开发者去关注。然则大家在那里可以不难说喜宝下。大家都掌握js对象足以相互引用,在某个对象申请了一块内存后,它很可能会被此外对象应用,而任何对象又被其余的靶子应用,一层一层,但它们的指针都是指向同一块内存的,大家把那最初引用的那块内存就足以改为GC根。用代码表示是那样的:

JavaScript

var obj = {a:1}; obj.pro = { a : 100 }; obj.pro.pro = { b : 200 }; var
two = obj.pro.pro; //那种景色下 {b:200}
就是被two引用到了,{b:200}对象引用的内存就是CG根

1
2
3
4
5
var obj = {a:1};
obj.pro = { a : 100 };
obj.pro.pro = { b : 200 };
var two = obj.pro.pro;
//这种情况下 {b:200} 就是被two引用到了,{b:200}对象引用的内存就是CG根

用一张官方的图可以如下表示:

澳门金沙国际 5

结合那张关系网的因素有二种:
Nodes:节点,对应一个对象,用成立该对象的构造方法来命名
Edges:连接线,对应着对象间的引用关系,用对象属性名来命名
从上图你也足以看看了第二列的表头Dishtance的意义是何等,没错,它指的就是CG根和引用对象时期的相距。根据这条表明,图中的对象5到CG根的离开就是2!那么哪些是直接占用内存(Shallow
size
)和最大占用内存(Retained
size
)呢?直接占用内存指的是目的自我占用的内存,因为对象在内存中会通过三种办法存在着,一种是被一个其余对象保留(大家可以说这一个目的依赖其他对象)或者被Dom对象那样的原生对象涵盖保留。在此地平昔占用内存指的就是前一种。(常常来讲,数组和字符串会保留愈来愈多的直白占用内存)。而最大内存(Retained
size
)就是该对象依赖的任何对象所占用的内存。你要明了那一个都是合法的分解,所以就是你以为云里雾里也是常规的,官方解释肯定是官腔嘛。按照卤煮自己的敞亮是这么的:

JavaScript

function a() { var obj = [1,2,…….n]; return function() {
//js效能域的原因,在此闭包运行的前后文中可以访问到obj这一个目的console.log(obj); } } //正常处境下,a函数执行完结obj占用的内存会被回收,不过此间a函数再次回到了一个函数表明式(见汤姆公公的博客函数表明式和函数评释),其中obj因为js的功效域的特殊性一贯留存,所以大家可以说b引用了obj。
var b = a(); //每一趟执行b函数的时候都得以访问到obj,表明内存未被回收
所以对于obj来说直接占用内存[1,2,….n],
而b看重obj,所obj是b的最大内存。 b()

1
2
3
4
5
6
7
8
9
10
11
function a() {
    var obj = [1,2,…….n];
    return function() {
        //js作用域的原因,在此闭包运行的上下文中可以访问到obj这个对象
        console.log(obj);
    }
}
//正常情况下,a函数执行完毕 obj占用的内存会被回收,但是此处a函数返回了一个函数表达式(见Tom大叔的博客函数表达式和函数声明),其中obj因为js的作用域的特殊性一直存在,所以我们可以说b引用了obj。
var b = a();
//每次执行b函数的时候都可以访问到obj,说明内存未被回收 所以对于obj来说直接占用内存[1,2,….n], 而b依赖obj,所obj是b的最大内存。
b()

在dom中也存在着引用关系:大家经过代码来看下那种引用关系:

JavaScript

<html> <body> <div id=”refA”> <ul>
<li><a></a></li>
<li><a></a></li> <li><a
id=”#refB”></a></li> </ul> </div>
<div></div> <div></div> </body>
</html> <script> var refA = document.getElementById(‘refA’);
var refB =
document.getElementById(‘refB’);//refB引用了refA。它们之间是dom树父节点和子节点的涉及。
</script>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<html>
    <body>
        <div id="refA">
            <ul>
                <li><a></a></li>
                <li><a></a></li>
                <li><a id="#refB"></a></li>
            </ul>
        </div>
        <div></div>
        <div></div>
    </body>
</html>
 
<script>
    var refA = document.getElementById(‘refA’);
    var refB = document.getElementById(‘refB’);//refB引用了refA。它们之间是dom树父节点和子节点的关系。
</script>

现今,难点来了,如果自己现在在dom中移除div#refA会如何呢?答案是dom内存依然留存,因为它被js引用。那么我把refA变量置为null呢?答案是内存仍然存在了。因为refB对refA存在引用,所以唯有在把refB释放,否则dom节点内存会一直存在浏览器中不可以被回收掉。上图:

澳门金沙国际 6

从而你看来Constructor这一列中目标借使有紫色背景就意味着有可能被JavaScript引用到不过没有被回收。以上只是卤煮个人通晓,假若不对劲,请您一定要升迁卤煮好即时更新,免得误人子弟!接着上文,Objects
Count
这一列是什么样意思啊?Objects
Count
这一列的含义比较好了解,从字面上大家就领会了其意义。就是目标实例化的数目。用代码表示就是那样的:

JavaScript

var ConstructorFunction = function() {};//构造函数 var a = new
ConstructorFunction();//第二个实例 var b = new
ConstructorFunction();//首个实例 ……. var n = new
ConstructorFunction();//第n个实例

1
2
3
4
5
var ConstructorFunction = function() {};//构造函数
var a = new ConstructorFunction();//第一个实例
var b = new ConstructorFunction();//第二个实例
…….
var n = new ConstructorFunction();//第n个实例

可以看到构造函数在上头有n个实例,那么对应在Objects
Count
那列里面就会有数字n。在此间,ConstructorFunction是我们友好定义的构造函数。那么这一个构造函数在何地吧,聪明的你一定可以猜到就在第一列Constructor中。实际上你可以看看列表中的Constructor这一列,其中大多数都是系统级其余构造函数,有一部分也是大家和好编排的:

  global property – 全局对象(像
‘window’)和引用它的目的时期的中间对象。若是一个目的由构造函数Person生成并被全局对象引用,那么引用路径就是那样的:[global]
> (global property >
Person。那跟一般的平素引用相互的对象分化。我们用中间对象是有总体性方面的案由,全局对象改变会很频仍,非全局变量的特性访问优化对全局变量来说并不适用。
  roots –
constructor中roots的始末引用它所选中的靶子。它们也可以是由引擎自主创办的片段引用。这几个引擎有用于引用对象的缓存,不过这一个引用不会阻止引用对象被回收,所以它们不是实在的强引用(FIXME)。
  closure – 一些函数闭包中的一组对象的引用
  arraystringnumberregexp –
一组属性引用了Array,String,Number或正则表明式的目的类型
  compiled code – 简单的话,所有东西都与compoled
code
关于。Script像一个函数,但实际对应了<script>的始末。SharedFunctionInfos
(SFI)是函数和compiled
code之间的对象。函数日常有内容,而SFIS没有(FIXME)。
HTMLDivElement, HTMLAnchorElement, DocumentFragment 等 –
你代码中对elements或document对象的引用。

点击展开它们查看详细项,@符号表示该目的ID。:

澳门金沙国际 7

一个快照可以有多少个总计,在左边区域的右上角我们得以看看点击下拉菜单可以得到多少个个职务视图选项:

澳门金沙国际 8

他们分别表示:
  Summary(概要) – 通过构造函数名分类展现对象;
  Comparison(对照) – 突显四个快照间对象的歧异;
  Containment(控制) – 探测堆内容;
  Statistic(图形表)-用图表的法门浏览内存使用概要

Comparison是指相比快照之间的差别,你可以率先拍一个快照A,操作网页一段时间后拍下此外一个快照B,然后在B快照的右手距区域的左上角选拔该选项。然后就可以看看比较图。上边显示的是每个列,每一项的变动。在自查自纠视图下,五个快照之间的不比就会展现出来了。当举办一个总类目后,扩张和删除了的目的就显得出来了:

澳门金沙国际 9

品味一下官方示例接济你询问相比的功能。

你也可以尝试着查看Statistic拔取,它会以图表的措施讲述内存概略。

澳门金沙国际 10

三、Record Heap Allocations.(对象跟踪器)

好了,首个功效也介绍完了,最终让大家来瞧瞧最后一个职能Record Heap
Allocations
.这几个效应是干啥的啊。它的作用是为为我们拍下一密密麻麻的快照(频率为50ms),为大家检测在启用它的时候每个对象的生存意况。形象一点说就是一旦拍摄内存快照的功力是摄像那么它效益相当于摄像。当大家启用start按钮的时候它便发轫拍照,直到停止。你会看到左侧区域上半有些有局地蓝色和黑色的柱条。藏蓝色的代表您监督那段时间内活跃过的靶子,不过被回收掉了。红色的表示依旧没有没回收。你照旧可以滑动滚轮缩放时间轴。

澳门金沙国际 11

对象跟踪器功能的裨益在于您可以连接不停的跟踪对象,在截至时,你可以选用某个时间段内(比如说灰色条没有变灰)查看里面活跃的对象。协助您一定内存败露难题。

四、结束 

好了,大约把Profiles讲完了。那东西对我们寻找内存败露来说依旧蛮有功能的。对于工具以来,紧假设多用,熟能生巧嘛。倘若您认为不惬意,我推荐你去读书法定文档,里面有N多的例证,N多的辨证,非凡详细。前提是您能跳到墙外去。当然也有翻译文档(卤煮的孤本都给您了,推荐一下吧)。最终真的是要像一片文章里面写的一样“感谢发明总计机的人,让大家那么些剪刀加浆糊的学问土匪变成了复制加粘贴版的学术海盗。”下期是ConsoleAudits。敬请关心。

2 赞 10 收藏
评论

澳门金沙国际 12

垃圾回收机制的品种

函数中的局地变量的生命周期:局地变量只在函数执行的进度中留存。而在这几个进度中,会为局地变量在栈(或堆)内存上分配相应的长空,以便存储它们的值。然后在函数中应用那么些变量,直至函数执行达成。此时,局部变量就向来不存在的画龙点睛了,因而可以释放它们的内存以供以后利用。在这种场合下,很容易看清变量是或不是还有存在的必不可少;但毫无所有意况下都那样不难就能得出结论。垃圾回收器必须盯住哪个变量有用,哪个变量没用,对于不再灵光的变量打上标记,以备将来撤消其占据的内存。用于标识无用变量的方针可能会因完结而异,但现实到浏览器中的落成,则一般有多个政策。

js中最常用的废品回收措施就是标志清除。当变量进入环境时,例如,在函数中声称一个变量,就将以此变量标记为“进入环境”。从逻辑上讲,永远无法假释进入环境的变量所占有的内存,因为一旦举办流进来相应的环境,就可能会用到它们。而当变量离开环境时,则将其标志为“离开环境”。

垃圾回收器在运作的时候会给存储在内存中的所有变量都增加记号(当然,可以拔取任何标志格局)。然后,它会去掉环境中的变量以及被环境中的变量引用的变量的符号(闭包)。而在此之后再被拉长记号的变量将被视为准备删除的变量,原因是条件中的变量已经不可能访问到那些变量了。最终,垃圾回收器达成内存清除工作,销毁这么些带标记的值并回收它们所占据的内存空间。

到二〇〇八年落成,IE、Firefox、Opera、Chrome、Safari的js达成应用的都是符号清除的废品回收策略或看似的国策,只不过垃圾收集的时光距离互差异。

引用计数的意义是跟踪记录每个值被引用的次数。当注脚了一个变量并将一个引用类型值赋给该变量时,则那些值的引用次数就是1。借使同一个值又被赋给另一个变量,则该值的引用次数加1。相反,假若带有对那一个值引用的变量又得到了其余一个值,则这几个值的引用次数减1。当这一个值的引用次数变成0时,则表明没有艺术再拜访那么些值了,由此就可以将其占用的内存空间回收回来。那样,当垃圾回收器下次再运行时,它就会放出那个引用次数为0的值所占有的内存。

Netscape
Navigator3是最早采纳引用计数策略的浏览器,但高速它就遇上一个严重的题材:循环引用。循环引用指的是目的A中涵盖一个针对性对象B的指针,而目标B中也隐含一个针对对象A的引用。

JavaScript

function fn() { var a = {}; var b = {}; a.pro = b; b.pro = a; } fn();

1
2
3
4
5
6
7
8
function fn() {
  var a = {};
  var b = {};
  a.pro = b;
  b.pro = a;
}
 
fn();

以上代码a和b的引用次数都是2,fn()执行落成后,七个对象都已经离开环境,在标记清除格局下是不曾难题的,但是在引用计数策略下,因为a和b的引用次数不为0,所以不会被垃圾回收器回收内存,如若fn函数被大批量调用,就会促成内存泄露

俺们清楚,IE中有部分目的并不是原生js对象。例如,其DOM和BOM中的对象就是运用C++以COM对象的款型落到实处的,而COM对象的杂质回收机制采取的就是援引计数策略。由此,固然IE的js引擎选用标记清除策略来实现,但js访问的COM对象仍旧是基于引用计数策略的。换句话说,只要在IE中提到COM对象,就会设有循环引用的题材。

JavaScript

var element = document.getElementById(“some_element”); var myObject =
new Object(); myObject.e = element; element.o = myObject;

1
2
3
4
var element = document.getElementById("some_element");
var myObject = new Object();
myObject.e = element;
element.o = myObject;

那些事例在一个DOM元素(element)与一个原生js对象(myObject)之间创设了循环引用。其中,变量myObject有一个名为element的品质指向element对象;而变量element也有一个属性名为o回指myObject。由于存在那几个轮回引用,尽管例子中的DOM从页面中移除,它也永远不会被回收。

为了防止类似那样的巡回引用难点,最好是在不行使它们的时候手工断开原生js对象与DOM元素之间的总是:

JavaScript

myObject.element = null; element.o = null;

1
2
myObject.element = null;
element.o = null;

将变量设置为null意味着切断变量与它原先引用的值时期的连日。当废品回收器下次运行时,就会删除那么些值并回收它们占有的内存。

1 赞 5 收藏
评论

4.化解方案

即便JavaScript会活动垃圾收集,不过假设大家的代码写法不当,会让变量一向处于“进入环境”的图景,不可以被回收。上面列一下内存败露常见的二种情景。

1、意外的全局变量引起的内存泄漏

function leaks(){

        leak = ‘xxxxxx’;//leak成为一个全局变量,不会被回收

}

2、闭包引起的内存泄漏

function bindEvent(){

        var obj=document.createElement(“XXX”);

        obj.onclick=function(){

                //Even if it’s a empty function

        }

}

闭包可以维持函数内一些变量,使其得不到释放。上例定义事件回调时,由于是函数内定义函数,并且其中函数–事件回调的引用外暴了,形成了闭包,解决之道,将事件处理函数定义在外部,解除闭包,或者在概念事件处理函数的表面函数中,删除对dom的引用

//将事件处理函数定义在外表

function bindEvent() {

        var obj=document.createElement(“XXX”);

        obj.onclick=onclickHandler;

}

function onclickHandler(){

       //do something

}

//在概念事件处理函数的表面函数中,删除对dom的引用

function bindEvent() {

        var obj=document.createElement(“XXX”);

        obj.onclick=function(){

                //Even if it’s a empty function

        }

        obj=null;

}

3、没有清理的DOM元素

var elements = {

       button: document.getElementById(‘button’),

        image: document.getElementById(‘image’),

        text: document.getElementById(‘text’)

};

function doStuff() {

        image.src = ”;

        button.click();

        console.log(text.innerHTML);

}

function removeButton() {

        document.body.removeChild(document.getElementById(‘button’));

}

即便如此大家用removeChild移除了button,但是还在elements对象里保存着#button的引用,换言之,
DOM元素还在内存里面。

4、被忘记的定时器或者回调

var someResource = getData();

setInterval(function() {

        var node = document.getElementById(‘Node’);

            if(node) {

                  node.innerHTML = JSON.stringify(someResource));

            }

}, 1000);

如此那般的代码很宽泛,若是id为Node的要素从DOM中移除,该定时器仍会设有,同时,因为回调函数中包蕴对someResource的引用,定时器外面的someResource也不会被假释。

5、子元素存在引用引起的内存泄漏

色情是指直接被js变量所引述,在内存里

丁巳革命是指直接被js变量所引用,如上图,refB被refA直接引用,导致即便refB变量被清空,也是不会被回收的

子元素refB由于parentNode的直接引用,只要它不被删除,它有着的父元素(图中革命部分)都不会被剔除

JavaScript内存泄漏

 

1、什么是闭包、以及闭包所涉及的功能域链那里就不说了。

2、JavaScript垃圾回收机制 

     JavaScript不须要手动地释放内存,它选择一种电动垃圾回收机制(garbage
collection)。当一个对象无用的时候,即程序中无变量引用这几个目的时,就会从内存中释放掉那个变量。

Code
    var s = [ 1, 2 ,3];
    var s = null;
    //那样原本的数组[1 ,2 ,3]就会被保释掉了。

3、循环引用

     多少个对象 A 、B 、C

     AàBàC
:A的某一特性引用着B,同样C也被B的性质引用着。即便将A清除,那么B、C也被放飞。

     AàBàCàB
:那里增加了C的某一品质引用B对象,若是这是清除A,那么B、C不会被放飞,因为B和C之间时有暴发了巡回引用。

Code
    var a = {};
    a.pro = { a:100 };
    a.pro.pro = { b:100 };
    a = null ; 
    //那种场地下,{a:100}和{b:100}就同时也被放走了。
            
    var obj = {};
    obj.pro = { a : 100 };
    obj.pro.pro = { b : 200 };
    var two = obj.pro.pro;
    obj = null;    
    //那种情况下 {b:200}不会被保释掉,而{a:100}被保释了。

4、循环引用和闭包

Code
    function outer(){
        var obj = {};
        function inner(){ 
            //那里引用了obj对象
        }
        obj.inner = inner;
    }

那是一种及其隐蔽的循环引用,。当调用五遍outer时,就会在其里面创造obj和inner多个目标,obj的inner属性引用了inner;同样inner也引述了obj,那是因为obj照旧在innerFun的封闭环境中,准确的讲这是由于JavaScript特有的“作用域链”。
故此,闭包分外不难创立循环引用,幸运的是JavaScript可以很好的处理那种循环引用。

5、IE中的内存泄漏

   
IE中的内存泄漏有少数种,那里有详实的解释(),园子里也有翻译了()。

   
那里只谈谈其中一种,即循环引用所导致的内存泄漏,因为,那是一种最广大的情景。

   
当在DOM元素或一个ActiveX对象与平时JavaScript对象期间存在循环引用时,IE在出狱那类变量时存在非凡的忙碌,最好手动切断循环引用,这么些bug在IE
7中已经被修复了()。

   “IE 6 suffered from memory
leaks
when a circular reference between several objects, among which at least
one DOM node, was created. This problem has been solved in IE 7. ”

    如若地点的例子(第4点)中obj引用的不是一个JavaScript
Function对象(inner),而是一个ActiveX对象或Dom元素,那样在IE中所形成的循环引用不可能赢得释放。

Code
    function init(){
        var elem = document.getElementByid( ‘id’ );
        elem.onclick = function(){
            alert(‘rain-man’);
            //那里引用了elem元素
        };
    }

Elem引用了它的click事件的监听函数,同样该函数通过其作用域链也引用回了elem元素。那样在IE中尽管距离当前页面也不会放出那几个循环引用。

6、解决办法

  
基本的点子就是手动清除那种循环引用,下边一个可怜不难的事例,实际使用时得以自己创设一个add伊芙nt()函数,并且在window的unload事件上对拥有事件绑定举行清除。

Code
    function outer(){
        var one = document.getElementById( ‘one’ );
        one.onclick = function(){};
    }
    window.onunload = function(){
        var one = document.getElementById( ‘one’ );
        one.onclick = null;
    };

 其他方式(by:Douglas Crockford)

Code
/**
 * 遍历某一因素节点及其具有后代元素
 *
 * @param Elem node  所要清除的要素节点
 * @param function func  举办拍卖的函数
 * 
 */
function walkTheDOM(node, func) {
    func(node); 
    node = node.firstChild; 
    while (node) { 
        walkTheDOM(node, func); 
        node = node.nextSibling; 
    } 

/**
 * 清除dom节点的具有引用,幸免内存走漏
 *
 * @param Elem node  所要清除的要素节点
 * 
 */
function purgeEventHandlers(node) {
    walkTheDOM(node, function (e) {
        for (var n in e) {            
            if (typeof e[n] === 
                    ‘function’) {
                e[n] = null;
            }
        }
    });

转自: 

垃圾回收机制(GC)

收起来说说垃圾回收机制(Garbage Collecation)。

在下面的率先个例子中,变量始终保留在内存中,说到底与JavaScript的杂质回收机制有关。JavaScript垃圾回收的编制很粗略:找出不再行使的变量,然后释放掉其占据的内存,不过那个历程不是实时的,因为其开发相比大,所以垃圾回收器会根据一定的时刻间隔周期性的实施。不再行使的变量也就是生命周期甘休的变量,当然只可能是一对变量,全局变量的生命周期直至浏览器卸载页面才会终止。局地变量只在函数的履行进度中存在,而在那一个进程中会为局地变量在栈或堆上分配相应的上空,以存储它们的值,然后在函数中应用那几个变量,直至函数截止,而闭包中由于其中函数的原委,外部函数并不能够算是截止。

仍然上代码表明呢:

JavaScript

function fn1() { var obj = {name: ‘hanzichi’, age: 10}; } function fn2()
{ var obj = {name:’hanzichi’, age: 10}; return obj; } var a = fn1(); var
b = fn2();

1
2
3
4
5
6
7
8
9
10
11
function fn1() {
  var obj = {name: ‘hanzichi’, age: 10};
}
 
function fn2() {
  var obj = {name:’hanzichi’, age: 10};
  return obj;
}
 
var a = fn1();
var b = fn2();

咱俩来看代码是什么样举行的。首先定义了五个function,分别名为fn1和fn2,当fn1被调用时,进入fn1的环境,会开发一块内存存放对象{name:
‘hanzichi’, age:
10},而当调用截至后,出了fn1的条件,那么该块内存会被js引擎中的垃圾回收器自动释放;在fn2被调用的进程中,再次来到的对象被全局变量b所指向,所以该块内存并不会被放走。

2.学问剖析

js的回收机制:垃圾回收机制—GC

Javascript具有电动垃圾回收机制(GC:Garbage
Collecation),也就是说,执行环境会负责管理代码执行进程中动用的内存。JavaScript垃圾回收的机制很容易:找出不再动用的变量,然后释放掉其占用的内存,然而这几个历程不是实时的,因为其支付比较大,所以垃圾回收器会依照定点的日子间隔周期性的施行。

究竟哪个变量是未曾用的?所以垃圾收集器必须盯住到底哪个变量没用,对于不再灵光的变量打上标记,以备未来撤回其内存。用于标记的无效变量的政策可能因达成而有所不同,平常状态下有三种完成格局:标记清除和引用计数。引用计数不太常用,标记清除较为常用。

1、标记清除

js中最常用的污物回收措施就是符号清除。当变量进入环境时,例如,在函数中宣示一个变量,就将这些变量标记为“进入环境”。从逻辑上讲,永远无法假释进入环境的变量所占有的内存,因为只要实施流进来相应的条件,就可能会用到它们。而当变量离开环境时,则将其标志为“离开环境”。

function test(){

        var a = 10 ; //被标记 ,进入环境

        var b = 20 ; //被标记 ,进入环境

}

test(); //执行达成 之后a、b又被标离开环境,被回收。

2、引用计数

引用计数的意思是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这么些值的引用次数就是1。即便同一个值又被赋给另一个变量,则该值的引用次数加1。相反,假诺带有对那些值引用的变量又取得了其余一个值,则那几个值的引用次数减1。当以此值的引用次数变成0时,则证实没有主意再拜访这么些值了,因此就足以将其占用的内存空间回收回来。那样,当废品回收器下次再运行时,它就会自由那一个引用次数为0的值所占据的内存。

function test(){

var a = {} ; //a的引用次数为0

var b = a ; //a的引用次数加1,为1

var c =a; //a的引用次数再加1,为2

var b ={}; //a的引用次数减1,为1

}

闭包拾遗

前边写了篇《闭包初窥》,谈了一些我对闭包的易懂认识,在前文基础上,补充并且更新些对于闭包的认识。

要么前边的非凡经典的例证,来填补些经典的诠释。

JavaScript

function outerFn() { var a = 0; function innerFn() { console.log(a++); }
return innerFn; } var fn = outerFn(); fn(); // 0 fn(); // 1

1
2
3
4
5
6
7
8
9
10
11
function outerFn() {
  var a = 0;
  function innerFn() {
    console.log(a++);
  }
  return innerFn;
}
 
var fn = outerFn();
fn(); // 0
fn(); // 1

此处并从未在outerFn内部修改全局变量,而是从outerFn中回到了一个对innerFn的引用。通过调用outerFn可以获取这几个引用,而且那么些引用可以可以保留在变量中。
那种就是离开函数效用域的意况下依旧可以透过引用调用内部函数的真实情形,意味着一旦存在调用内部函数的也许,JavaScript就须求保留被引述的函数。而且JavaScript运行时索要跟踪引用那一个里面函数的所有变量,直到最后一个变量扬弃,JavaScript的排泄物收集器才能释放相应的内存空间。

让我们说的更淋漓一些。所谓“闭包”,就是在协会函数体内定义别的的函数作为对象对象的艺术函数,而以此目标的不二法门函数反过来引用外层函数体中的临时变量。那使得只要目的对象在生存期内始终能有限支撑其方法,就能直接保持原构造函数体当时使用的暂时变量值。纵然起始河的构造函数调用已经终止,临时变量的名号也都冰释了,但在目的对象的主意内却一味能引用到该变量的值,而且该值只好通那种方式来做客。即便再一次调用相同的构造函数,但只会生成新对象和方法,新的暂时变量只是对应新的值,和上次那次调用的是各自独立的。

抑或前文的事例:

JavaScript

<ul> <li>0</li> <li>1</li>
<li>2</li> <li>3</li> <li>4</li>
</ul> <script> var lis =
document.getElementsByTagName(‘li’); for(var i = 0; i < lis.length;
i++) { ~function(num) { lis[i].onclick = function() { alert(num) };
}(i) } </script>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<ul>
  <li>0</li>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
</ul>
<script>
  var lis = document.getElementsByTagName(‘li’);
  for(var i = 0; i < lis.length; i++) {
    ~function(num) {
      lis[i].onclick = function() {
        alert(num)
      };
    }(i)
  }
</script>

干什么不加立刻实施函数,alert的都会是5吗?

设若不加IIFE,当i的值为5的时候,判断标准不树立,for循环执行完毕,不过因为种种li的onclick方法那时候为其中函数,所以i被闭包引用,内存不可能被销毁,i的值会一贯维系5,直到程序改变它仍旧具有的onclick函数销毁(主动把函数赋为null或者页面卸载)时才会被回收。这样每一遍我们点击li的时候,onclick函数会查找i的值(作用域链是援引方式),一查等于5,然后就alert给大家了。加上IIFE后即是又创建了一层闭包,函数注明放在括号内就改成了表明式,后边再增加括号就是调用了,那时候把i当参数传入,函数马上施行,num保存每趟i的值。

7.参考文献

参考一:javascript的污染源回收机制与内存管理http://www.jb51.net/article/75292.htm

参照二:js内存泄漏常见的各种境况

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图