【转贴】精通 JavaScript 应用程序中的内存泄漏政治学

【转贴】精通 JavaScript 应用程序中的内存泄漏政治学

清单 8. assets/scripts/main.js

图 5. 出于保留引用导致的内存泄漏
政治学 1 

您可 下载 本文中运用的示范的源代码。

末段,更新 main.js
以设置注册表,并将对注册表的一个引用传递给 leaker 父对象,如清单 12
所示。

清单 4. assets/scripts/leaker.js

 

至于小编

清单 13. assets/scripts/leaker.js

蕴涵 jQuery
是为着确保一种管监护人件绑定的简约语法适合不一致的浏览器,而且严酷依据最普遍的开销实践。为 leaker 类和严重性
JavaScript 方法添加脚本标记。在付出环境中,将 JavaScript
文件合并到单个文件中司空见惯是一种更好的做法。出于本示例的用处,将逻辑放在独立的公文中更便于。

一种将目的保留在内存中的不太强烈的艺术是将它记录到控制奥兰多。清单 6
更新了 Leaker 类,突显了此格局的一个演示。

将该类导入 index.html 页面中,放在 leaker.js 此前,如清单 10 所示。

Ben Dolmar 自 2001 年起就起来了正规编程,拥有丰裕的技巧经历,包括ActionScript、iOS、JavaScript、PHP、Ruby 和图片设计。自 1997
年从马里兰高校乌兰巴托分校结业并得到音讯和政治学双学位后,Ben 在 1998
年到 2007 年里面担任 Faith Inkubators 的成品老总。自 2007 年插足 The
Nerdery 以来,他已帮带启动了 400 多少个连串。在 2002 年,Ben
被提醒为首席软件工程师。他曾在 SWF坎普 举办过 ActionScript
方面的讲演,参加过在米兰举行的有关不断演变的 Web 标准和 HTML 5 的
SocialMediaDev Camp。

 

销毁对象和对象所有权

当下,只有 谷歌 Chrome(提供了 Heap
Profile)达成了一个内存管理工具作为它的开发人士工具。我在本文中行使 Heap
Profiler 测试和示范 JavaScript 运行时怎么处理内存。

               
var Leaker = function(){};

Leaker.prototype = {
    init:function(name, parent){
        this._name = name;
        this._parent = parent;
        this._child = null;
        this.createChildren();
    },

    createChildren:function(){
        if(this._parent !== null){
            // Only create a child if this is the root
            return;
        }
        this._child = new Leaker();
        this._child.init("leaker 2", this);
    },

    destroy: function(){

    }
};
               
destroy: function(){
    if(this._child !== null){
        this._child.destroy();            
    }
    this._registry.remove(this);
}

要打听哪些防止内存泄漏,需求驾驭对象的中坚生命周期。当创设一个对象时,JavaScript
会自动为该目的分配适当的内存。从这一阵子起,垃圾回收器就会不断对该指标开展评估,以查看它是或不是仍是实用的靶子。

**图 3. 快照实例**  
![](http://www.ibm.com/developerworks/cn/web/wa-jsmemory/fig03.jpg)   

你可以过滤 Heap Profiler
来仅浮现特殊类的实例。为了接纳该功效,成立一个新类来封装泄漏对象的表现,而且以此类很简单在
Heap Profiler 中找到,如清单 2 所示。

结束语

对象生命周期

转贴:http://www.ibm.com/developerworks/cn/web/wa-jsmemory/

清单 6. assets/scripts/leaker.js

在多少个对象相互引用且互相保留时,就会生出一个循环往复,如图 4 所示。

检测和解决内存难点

 

 

清单 7 呈现了一个简练的代码示例。

内存泄漏 1:闭包

回页首

清单 9. assets/scripts/registry.js

现今,您已预备好创造一个目的,在内存中查阅它,然后释放它。

描述 名字 大小 下载方法
文章源代码 JavascriptMemoryManagementSource.zip 4KB HTTP

当处理 JavaScript
那样的脚本语言时,很不难忘记每个对象、类、字符串、数字和办法都亟需分配和保留内存。语言和运作时的杂质回收器隐藏了内存分配和假释的具体细节。

在创设内存泄漏之前,请查看一次适当收集内存的简要交互。首先创立一个包罗五个按钮的几乎HTML 页面,如清单 1 所示。

 

destroy 方法平日是清理一个对象的画龙点睛步骤,但在半数以上景观下它还不够。在争鸣上,在销毁相关实例后,保留对已灭相对象的引用的其他对象可调用自身之上的法子。因为那种情形或许会时有暴发不可预测的结果,所以仅在对象即将无用时调用
destroy 方法,那根本。

 

 

清单 11. assets/scripts/leaker.js

               
<html>
<head>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" 
type="text/javascript"></script>
</head>
<body>
    <button id="start_button">Start</button>
    <button id="destroy_button">Destroy</button>
    <script src="assets/scripts/leaker.js" type="text/javascript" 
charset="utf-8"></script>
    <script src="assets/scripts/main.js" type="text/javascript" 
charset="utf-8"></script>
</body>
</html>

万岁!垃圾回收有效的。现在是时候破坏它了。

 

  • developerWorks
    社区
    :查看开发人士拉动的博客、论坛、群组和维基,并与任何
    developerWorks 用户沟通。

内存泄漏指任何对象在你不再具有或索要它今后依旧存在。在近年几年中,许多浏览器都句斟字酌了在页面加载进程中从
JavaScript
回收内存的力量。可是,并不是有着浏览器都存有同样的运转格局。Firefox
和旧版的 Internet Explorer
都设有过内存泄漏,而且内存败露一向不断到浏览器关闭。

诚如而言,destroy
方法最佳使用是在一个对象有一个尽人皆知的所有者来负担它的生命周期时。此处境常常存在于分层系统中,比如
MVC 框架中的视图或控制器,或者一个画布彰显系统的场景图。

洋洋效果无需考虑内存管理即可兑现,但却不经意了它恐怕在先后中拉动主要的题材。不当清理的目的可能会存在比预想要长得多的时辰。那一个目的继续响应事件和消耗资源。它们可强制浏览器从一个虚构磁盘驱动器分配内存页,那眼看影响了微机的进程(在无限的气象中,会促成浏览器崩溃)。

观看浏览器的内存占用只好相当粗略地突显 JavaScript
应用程序的实在内存使用。浏览器数据不会告诉您哪些对象暴发了泄漏,也无从有限支撑数据与您应用程序的的确内存占用确实匹配。而且,由于一些浏览器中设有落到实处难题,DOM
元素(或备用的利用程序级对象)可能不会在页面中销毁相应元素时释放。摄像标记尤为如此,摄像标记须求浏览器已毕一种更加小巧的基础架构。

               
var Registry = function(){};

Registry.prototype = {
    init:function(){
        this._subscribers = [];
    },

    add:function(subscriber){
        if(this._subscribers.indexOf(subscriber) >= 0){
            // Already registered so bail out
            return;
        }
        this._subscribers.push(subscriber);
    },

    remove:function(subscriber){
        if(this._subscribers.indexOf(subscriber) < 0){
            // Not currently registered so bail out
            return;
        }
              this._subscribers.splice(
                  this._subscribers.indexOf(subscriber), 1
              );
    }
};

不经过测试,就不容许准确度量内存使用。很简单使循环引用占据对象曲线图中的半数以上区域。Chrome
的 Heap Profiler
是一个确诊内存难点的弥足尊崇工具,在付出时定期选拔它也是一个不利的选用。在推断目的曲线图中要自由的求实资源时请设定具体的预想,然后开展认证。任曾几何时候当您看看不想要的结果时,请仔细调查。

  1. 将注意力重临到 Web 上,选拔 Destroy
  2. 取得第八个堆快照。
  3. 过滤第五个快照,查找 Leaker 类的实例,找不到其余实例。

    在加载第五个快照时,也可将分析形式从 Summary 切换到Comparison,并对比第多少个和第四个快照。您会晤到偏移值
    -1(在两遍快照之间自由了 Leaker 对象的一个实例)。

清单 1. index.html

**图 2. Profiles 选项卡**  
![](http://www.ibm.com/developerworks/cn/web/wa-jsmemory/fig02.jpg)   
               
var Leaker = function(){};
Leaker.prototype = {

    init:function(name, parent, registry){
        this._name = name;
        this._registry = registry;
        this._parent = parent;
        this._child = null;
        this.createChildren();
        this.registerCallback();
    },

    createChildren:function(){
        if(this._parent !== null){
            // Only create child if this is the root
            return;
        }
        this._child = new Leaker();
        this._child.init("leaker 2", this, this._registry);
    },

    registerCallback:function(){
        this._registry.add(this);
    },

    destroy: function(){
        this._registry.remove(this);
    }
};

清单 7. assets/scripts/leaker.js

内存泄漏 3:循环

 

清单 12. assets/scripts/main.js

学习

只是,如若引入了第四个保留该子对象的靶子,该循环会导致内存泄漏。例如,创建一个 registry 对象,如清单
9 所示。

 

简介

 

回页首

回页首

内存泄漏 2:控制台日志

一种科学的做法是,创设一个规范措施来担负让一个目的有资格被垃圾回收。destroy
功用的首要用途是,集中清理该目标完毕的有着以下后果的操作的天职:

 

垃圾堆回收器定期扫描对象,并盘算引用了各类对象的别的对象的多寡。即使一个目的的引用数量为
0(没有其他对象引用过该对象),或对该目的的绝世引用是循环的,那么该对象的内存即可回收。图
1 呈现了垃圾回收器回收内存的一个演示。

清单 5. assets/scripts/main.js

关于下载方式的音信

 

一种预防一个对象被垃圾回收的简要方法是设置一个在回调中援引该目的的间距或过期。要翻开实际使用,可更新
leaker.js 类,如清单 4 所示。

Root 对象的实例化可以修改,如清单 8 所示。

 

清单 10. index.html

图 4. 创制一个循环往复的引用
政治学 2 

 

参考资料

政治学 3

偶然,多个没有丰盛紧密关系的目的之间也会设有循环,其中一个对象管理另一个对象的生命周期。在那样的图景下,在那三个对象时期成立关联的目的应负担在团结被销毁时暂停循环。

得到产品和技巧

回页首

 

  1. 签到到目录页面。
  2. 单击 Start
  3. 转到控制台并确认 Leaking 对象已被跟踪。
  4. 单击 Destroy
  5. 回去控制台并键入 leak,以记录全局变量当前的内容。此刻该值应为空。
  6. 收获另一个堆快照并过滤 Leaker 对象。

    您应预留一个 Leaker 对象。

  7. 归来控制台并免除它。

  8. 创建另一个堆配置文件。

    在清理控制台后,保留 leaker 的陈设文件应已清除。

               
var Leaker = function(){};

Leaker.prototype = {
    init:function(){
        console.log("Leaking an object: %o", this);
    },

    destroy: function(){

    }      
};

registry 类是让任何对象向它注册,然后从注册表中删除自身的目的的简约示例。即便这么些新鲜的类与注册表毫无关联,但那是事件调度程序和通知系统中的一种普遍情势。

  1. 在 Chrome 中加载索引页面。

    因为你是直接从 谷歌(Google) 加载 jQuery,所以须要三番五次互连网来运转该样例。

  2. 开辟开发人士工具,方法是开辟 View 菜单并选取 Develop
    子菜单。接纳 Developer Tools 命令。

  3. 转到 Profiles 选项卡并拿走一个堆快照,如图 2 所示。
               
var Leaker = function(){};
Leaker.prototype = {
    init:function(){

    }    
};

如果在开立和销毁对象后执行三遍堆分析,您应该会看到垃圾收集器检测到了这些轮回引用,并在你选用Destroy 按钮时释放了内存。

 

可选用以下步骤来演示控制台的熏陶。

  • 堵住它的引用计数下跌到
    0(例如,删除存在难题的轩然大波侦听器和回调,并从此外劳动打消注册)。
  • 运用不须求的 CPU 周期,比如间隔或动画。

比如,若是在 Mac OSX 上安装了 XCode,您能够启动 Instruments
应用程序,并将它的移位监视器工具附加到你的浏览器上,以开展实时分析。在
Windows®
上,您可以行使任务管理器。假如在你使用应用程序的经过中,发现内存使用量随时间变化的曲线逐步回升,那么你就精晓存在内存泄漏。

               
$("#start_button").click(function(){
    if(leak !== null || leak !== undefined){
        return;
    }
  leak = new Leaker();
  leak.init();
});

$("#destroy_button").click(function(){
    leak = null;
});

var leak = new Leaker();

在本文中,了然对象的为主生命周期,垃圾回收怎么着确定一个目标是还是不是被放出,以及如何评估潜在的透漏行为。其余,学习怎么行使
谷歌 Chrome 中的 Heap Profiler
来诊断内存难题。一些示范呈现了什么化解闭包、控制台日志和巡回带来的内存泄漏。

控制台日志记录对完全内存配置文件的熏陶可能是广大开发人士都未想到的万分关键的难点。记录错误的对象足以将大气数额保存在内存中。注意,那也适用于:

下载

现在,当重复 上一节 中的第
1-9
步时,您应在第七个快照中寓目,Leaker 对象被持久化,并且该间隔会永远持续运行。那么暴发了怎样?在一个闭包中援引的其他部分变量都会被该闭包保留,只要该闭包存在就永远保存。要有限扶助对 setInterval 方法的回调在走访
Leaker
实例的界定时实施,须求将 this 变量分配给一部分变量 self,那几个变量用于从闭包内触发 onInterval。当 onInterval 触发时,它就可见访问 Leaker 对象中的任何实例变量(包含它本身)。然而,只要事件侦听器存在,Leaker 对象就不会被垃圾回收。

过去导致内存泄漏的成百上千经文格局在当代浏览器中以不再导致泄漏内存。不过,近年来有一种分歧的动向影响着内存泄漏。许四人正筹划用来在平昔不硬页面刷新的单页中运行的
Web
应用程序。在那样的单页中,从应用程序的一个动静到另一个动静时,很不难保留不再须求或不相干的内存。

               
leak = new Leaker(); 
leak.init("leaker 1", null);

回页首

               
      $("#start_button").click(function(){
  var leakExists = !(
          window["leak"] === null || window["leak"] === undefined
      );
  if(leakExists){
      return;
  }
  leak = new Leaker();
  leak.init("leaker 1", null, registry);
});

$("#destroy_button").click(function(){
    leak.destroy();
    leak = null;
});

registry = new Registry();
registry.init();

现今,当执行堆分析时,您应看到每一遍接纳 Start
按钮时,会创制并保留 Leaker 对象的八个新实例。图 5
突显了对象引用的流水线。

要化解此题材,可在清空所蕴藏的 leaker 对象引用以前,触发添加到该目的的 destroy 方法,方法是翻新
Destroy 按钮的单击处理程序,如清单 5 所示。

 

回页首

回页首

               
var Leaker = function(){};

Leaker.prototype = {
    init:function(){
        this._interval = null;
        this.start();
    },

    start: function(){
        var self = this;
        this._interval = setInterval(function(){
            self.onInterval();
        }, 100);
    },

    destroy: function(){
        if(this._interval !== null){
            clearInterval(this._interval);          
        }
    },

    onInterval: function(){
        console.log("Interval");
    }
};

 

               
<script src="assets/scripts/registry.js" type="text/javascript" 
charset="utf-8"></script>

回页首

美丽的化解方案是浏览器供应商在浏览器中提供一组工具,协助您监视内存使用,识别泄漏的对象,以及确定为何一个尤其目的仍标记为保存。

固然有多种格局来缓解此类难题,但在此处境下,最简易的办法是更新 Leaker 类,以在销毁它时销毁它的子对象。对于本示例,更新 destroy 方法(如清单
13 所示)就丰硕了。

在创造对象时要布置该目标的清理工作,这比在其后将一个清理阶段移植到应用程序中要便于得多。日常要布置删除事件侦听器,并截止您成立的间距。要是认识到了您应用程序中的内存使用,您将获取更牢靠且品质更高的应用程序。

 

观察该系统的骨子里运用会很有援助,但提供此意义的工具很不难。了解你的
JavaScript
应用程序占用了稍稍内存的一种办法是行使系统工具查看浏览器的内存分配。有七个工具可为您提供当前的接纳,并勾画一个历程的内存使用量随时间变化的主旋律图。

               
$("#destroy_button").click(function(){
    leak.destroy();
    leak = null;
});

清单 3. assets/scripts/main.js

Ben
Dolmar
,
软件开发人士, The Nerdery

图 1. 通过垃圾收集回收内存
政治学 4 

 

简介: 垃圾回收解放了俺们,它让我们可将精力集中在应用程序逻辑(而不是内存管理)上。不过,垃圾收集并不神奇。通晓它的干活规律,以及怎么着使它保留本应在很久从前释放的内存,就足以兑现更快更牢靠的应用程序。在本文中,学习一种固定
JavaScript
应用程序中内存泄漏的种类方法、二种普遍的透漏形式,以及解决这一个泄漏的适用格局。

清单 2. assets/scripts/leaker.js

从表面上看,它像一个不自然的以身作则,但它事实上卓殊广阔。越发经典的面向对象框架中的事件侦听器平时遵循类似图
5 的格局。那连串型的方式也恐怕与闭包和控制台日志导致的难题相关联。

 

人人曾很多次尝试在客户端 JavaScript
库中加上对内存分配的跟踪。不幸的是,所有尝试都不是特意可依赖。例如,流行的
stats.js
包由于禁止确性而一筹莫展支撑。一般而言,尝试从客户端维护或规定此音讯存在必然的题材,是因为它会在应用程序中引入开支且不能可相信地截至。

绑定 Start
按钮以初阶化 Leaker 对象,并将它分配给全局命名空间中的一个变量。还亟需将
Destroy
按钮绑定到一个应清理 Leaker目的的点子,并让它为垃圾收集做好准备,如清单
3 所示。

  1. 将注意力再次回到到 Web 上,选拔 Start
  2. 收获另一个堆快照。
  3. 过滤首个快照,查找 Leaker 类的实例,找不到其余实例。切换来第四个快照,您应该能找到一个实例,如图
    3 所示。

更新 Leaker 对象,以向注册表对象注册该对象自我(可能用来有关部分未落实事件的布告)。这开创了一个源点要保存的
leaker 子对象的 root 节点备用路径,但鉴于该循环,父对象也将保存,如清单
11 所示。

  • 在用户键入 JavaScript 时,在控制奥兰多的一个交互式会话期间记录的目的。
  • 政治学,由 console.log 和 console.dir 方法记录的对象。

 

讨论

固然 JavaScript
已被垃圾回收,依然会有无数主意会将不需求的对象保留在内存中。近日一大半浏览器都已立异了内存清理功用,但评估您应用程序内存堆的工具依旧有限(除了使用
GoogleChrome)。通过从不难的测试案例发轫,很简单评估潜在的走漏行为并规定是否存在败露。

解析堆快照

 

admin

网站地图xml地图