js怎么查看闭包js检测内存泄露露

JavaScript内存泄露的4种方式及如何避免
作者:佚名
分类 : 比特网
  内存泄露是每个开发者最终都要面对的问题,它是许多问题的根源:反应,崩溃,高延迟,以及其他应用问题。
  什么是内存泄露?
  本质上,内存泄露可以定义为:应用程序不再需要占用内存的时候,由于某些原因,内存没有被或可用内存池回收。编程语言管理内存的方式各不相 同。只有开发者最清楚哪些内存不需要了,操作系统可以回收。一些编程语言提供了语言特性,可以帮助开发者做此类事情。另一些则寄希望于开发者对内存是否需 要清晰明了。
  JavaScript 内存管理
  JavaScript 是一种垃圾回收语言。垃圾回收语言通过周期性地检查先前分配的内存是否可达,帮助开发者管理内存。换言之,垃圾回收语言减轻了“内存仍可用”及“内存仍可 达”的问题。两者的区别是微妙而重要的:仅有开发者了解哪些内存在将来仍会使用,而不可达内存通过算法确定和标记,适时被操作系统回收。
  JavaScript 内存泄露
  垃圾回收语言的内存泄露主因是不需要的引用。理解它之前,还需了解垃圾回收语言如何辨别内存的可达与不可达。
  Mark-and-sweep
  大部分垃圾回收语言用的算法称之为 Mark-and-sweep 。算法由以下几步组成:
  垃圾回收器创建了一个“roots”列表。Roots 通常是代码中的引用。JavaScript 中,“window” 对象是一个全局变量,被当作 root 。window 对象总是存在,因此垃圾回收器可以检查它和它的所有子对象是否存在(即不是垃圾);
  所有的 roots 被检查和标记为激活(即不是垃圾)。所有的子对象也被递归地检查。从 root 开始的所有对象如果是可达的,它就不被当作垃圾。
  所有未被标记的内存会被当做垃圾,收集器现在可以释放内存,归还给操作系统了。
  现代的垃圾回收器改良了算法,但是本质是相同的:可达内存被标记,其余的被当作垃圾回收。
  不需要的引用是指开发者明知内存引用不再需要,却由于某些原因,它仍被留在激活的 root 树中。在 JavaScript 中,不需要的引用是保留在代码中的变量,它不再需要,却指向一块本该被释放的内存。有些人认为这是开发者的错误。
  为了理解 JavaScript 中最常见的内存泄露,我们需要了解哪种方式的引用容易被遗忘。
  三种类型的常见 JavaScript 内存泄露
  1:意外的全局变量
  JavaScript 处理未定义变量的方式比较宽松:未定义的变量会在全局对象创建一个新变量。在中,全局对象是 window 。
  function foo(arg) {
bar = "this is a hidden global variable"; }
  真相是:
  function foo(arg) {
window.bar = "this is an explicit global variable"; }
  函数 foo 内部忘记使用 var ,意外创建了一个全局变量。此例泄露了一个简单的字符串,无伤大雅,但是有更糟的情况。
  另一种意外的全局变量可能由 this 创建:
  function foo() {
this.variable = "potential accidental global"; }
// Foo 调用自己,this 指向了全局对象(window) // 而不是 undefined foo();
  在 JavaScript 文件头部加上 'use strict',可以避免此类错误发生。启用严格模式解析 JavaScript ,避免意外的全局变量。
  全局变量注意事项
  尽管我们讨论了一些意外的全局变量,但是仍有一些明确的全局变量产生的垃圾。它们被定义为不可回收(除非定义为空或重新分配)。尤其当全局变量用于 临时和处理大量信息时,需要多加小心。如果必须使用全局变量存储大量数据时,确保用完以后把它设置为 null 或者重新定义。与全局变量相关的增加内存消耗的一个主因是缓存。缓存数据是为了重用,缓存必须有一个大小上限才有用。高内存消耗导致缓存突破上限,因为缓 存内容无法被回收。
  2:被遗忘的计时器或回调函数
  在 JavaScript 中使用 setInterval 非常平常。一段常见的代码:
  var someResource = getData(); setInterval(function() {
var node = document.getElementById('Node');
if(node) {
// 处理 node 和 someResource
node.innerHTML = JSON.stringify(someResource));
} }, 1000);
  此例说明了什么:与节点或数据关联的计时器不再需要,node 对象可以删除,整个回调函数也不需要了。可是,计时器回调函数仍然没被回收(计时器停止才会被回收)。同时,someResource 如果存储了大量的数据,也是无法被回收的。
  对于观察者的例子,一旦它们不再需要(或者关联的对象变成不可达),明确地移除它们非常重要。老的 IE 6 是无法处理循环引用的。如今,即使没有明确移除它们,一旦观察者对象变成不可达,大部分浏览器是可以回收观察者处理函数的。
  观察者代码示例:
  var element = document.getElementById('button'); function onClick(event) {
element.innerHTML = 'text'; }
element.addEventListener('click', onClick);
  对象观察者和循环引用注意事项
  老版本的 IE 是无法检测 DOM 节点与 JavaScript 代码之间的循环引用,会导致内存泄露。如今,现代的浏览器(包括 IE 和 Edge)使用了更先进的垃圾回收算法,已经可以正确检测和处理循环引用了。换言之,回收节点内存时,不必非要调用 removeEventListener 了。
  3:脱离 DOM 的引用
  有时,保存 DOM 节点内部数据结构很有用。假如你想快速更新表格的几行内容,把每一行 DOM 存成字典(JSON 键值对)或者数组很有意义。此时,同样的 DOM 元素存在两个引用:一个在 DOM 树中,另一个在字典中。将来你决定删除这些行时,需要把两个引用都清除。
  var elements = {
button: document.getElementById('button'),
image: document.getElementById('image'),
text: document.getElementById('text') };
function doStuff() {
image.src = 'http://some.url/image';
button.click();
console.log(text.innerHTML);
// 更多逻辑 }
function removeButton() {
// 按钮是 body 的后代元素
document.body.removeChild(document.getElementById('button'));
// 此时,仍旧存在一个全局的 #button 的引用
// elements 字典。button 元素仍旧在内存中,不能被 GC 回收。 }
  此外还要考虑 DOM 树内部或子节点的引用问题。假如你的 JavaScript 代码中保存了表格某一个
的引用。将来决定删除整个表格的时候,直觉认为 GC 会回收除了已保存的
以外的其它节点。实际情况并非如此:此 是表格的子节点,子元素与父元素是引用关系。由于代码保留了
的引用,导致整个表格仍待在内存中。保存 DOM 元素引用的时候,要小心谨慎。
  4:闭包
  闭包是 JavaScript 开发的一个关键方面:匿名函数可以访问父级作用域的变量。
  代码示例:
  var theThing = var replaceThing = function () {
var originalThing = theT
var unused = function () {
if (originalThing)
console.log("hi");
theThing = {
longStr: new Array(1000000).join('*'),
someMethod: function () {
console.log(someMessage);
setInterval(replaceThing, 1000);
  代码做了一件事情:每次调用 replaceThing ,theThing 得到一个包含一个大数组和一个新闭包(someMethod)的新对象。同时,变量 unused 是一个引用 originalThing 的闭包(先前的 replaceThing 又调用了 theThing )。思绪混乱了吗?最重要的事情是,闭包的作用域一旦创建,它们有同样的父级作用域,作用域是共享的。someMethod 可以通过 theThing 使用,someMethod 与 unused 分享闭包作用域,尽管 unused从未使用,它引用的 originalThing 迫使它保留在内存中(防止被回收)。当这段代码反复运行,就会看到内存占用不断上升,垃圾回收器(GC)并无法降低内存占用。本质上,闭包的链表已经创建,每一个闭包作用域携带一个指向大数组的间接的引用,造成严重的内存泄露。
  Meteor 的博文 解释了如何修复此种问题。在 replaceThing 的最后添加 originalThing = null 。
  Chrome 内存剖析工具概览
  Chrome 提供了一套很棒的检测 JavaScript 内存占用的工具。与内存相关的两个重要的工具:timeline 和 profiles。
  Timeline
  timeline 可以检测代码中不需要的内存。在此截图中,我们可以看到潜在的泄露对象稳定的增长,数据采集快结束时,内存占用明显高于采集初期,Node(节点)的总量也很高。种种迹象表明,代码中存在 DOM 节点泄露的情况。
  Profiles
  Profiles 是你可以花费大量时间关注的工具,它可以保存,对比 JavaScript 代码内存使用的不同快照,也可以记录时间分配。每一次结果包含不同类型的列表,与内存泄露相关的有 summary(概要) 列表和 comparison(对照) 列表。
  summary(概要) 列表展示了不同类型对象的分配及合计大小:shallow size(特定类型的所有对象的总大小),retained size(shallow size 加上其它与此关联的对象大小)。它还提供了一个概念,一个对象与关联的 GC root 的距离。
  对比不同的快照的 comparison 可以发现内存泄露。
  实例:使用 Chrome 发现内存泄露
  实质上有两种类型的泄露:周期性的内存增长导致的泄露,以及偶现的内存泄露。显而易见,周期性的内存泄露很容易发现;偶现的泄露比较棘手,一般容易被忽视,偶尔发生一次可能被认为是优化问题,周期性发生的则被认为是必须解决的 bug。
  以 Chrome 文档中的代码为例:
  var x = [];
function createSomeNodes() {
frag = document.createDocumentFragment();
for (;i & 0; i--) {
div = document.createElement("div");
div.appendChild(document.createTextNode(i + " - "+ new Date().toTimeString()));
frag.appendChild(div);
document.getElementById("nodes").appendChild(frag); }
function grow() {
x.push(new Array(1000000).join('x'));
createSomeNodes();
setTimeout(grow,1000); }
  当 grow 执行的时候,开始创建 div 节点并插入到 DOM 中,并且给全局变量分配一个巨大的数组。通过以上提到的工具可以检测到内存稳定上升。
  找出周期性增长的内存
  timeline 标签擅长做这些。在 Chrome 中打开例子,打开 Dev Tools ,切换到 timeline,勾选 memory 并点击记录按钮,然后点击页面上的 The Button 按钮。过一阵停止记录看结果:
  两种迹象显示出现了内存泄露,图中的 Nodes(绿线)和 JS heap(蓝线)。Nodes 稳定增长,并未下降,这是个显著的信号。
  JS heap 的内存占用也是稳定增长。由于垃圾收集器的影响,并不那么容易发现。图中显示内存占用忽涨忽跌,实际上每一次下跌之后,JS heap 的大小都比原先大了。换言之,尽管垃圾收集器不断的收集内存,内存还是周期性的泄露了。
  确定存在内存泄露之后,我们找找根源所在。
  保存两个快照
  切换到 Chrome Dev Tools 的 profiles 标签,刷新页面,等页面刷新完成之后,点击 Take Heap Snapshot 保存快照作为基准。而后再次点击 The Button 按钮,等数秒以后,保存第二个快照。
  筛选菜单选择 Summary,右侧选择 Objects allocated between Snapshot 1 and Snapshot 2,或者筛选菜单选择 Comparison ,然后可以看到一个对比列表。
  此例很容易找到内存泄露,看下 (string) 的 Size Delta Constructor,8MB,58个新对象。新对象被分配,但是没有释放,占用了8MB。
  如果展开 (string) Constructor,会看到许多单独的内存分配。选择某一个单独的分配,下面的 retainers 会吸引我们的注意。
  我们已选择的分配是数组的一部分,数组关联到 window 对象的 x 变量。这里展示了从巨大对象到无法回收的 root(window)的完整路径。我们已经找到了潜在的泄露以及它的出处。
  我们的例子还算简单,只泄露了少量的 DOM 节点,利用以上提到的快照很容易发现。对于更大型的网站,Chrome 还提供了 Record Heap Allocations 功能。
  Record heap allocations 找内存泄露
  回到 Chrome Dev Tools 的 profiles 标签,点击 Record Heap Allocations。工具运行的时候,注意顶部的蓝条,代表了内存分配,每一秒有大量的内存分配。运行几秒以后停止。
  上图中可以看到工具的杀手锏:选择某时间线,可以看到这个时间段的内存分配情况。尽可能选择接近峰值的时间线,下面的列表仅显示了三种 constructor:其一是泄露最严重的(string),下一个是关联的 DOM 分配,最后一个是 Text constructor(DOM 叶子节点包含的文本)。
  从列表中选择一个 HTMLDivElement constructor,然后选择 Allocation stack。
  现在知道元素被分配到哪里了吧(grow -& createSomeNodes),仔细观察一下图中的时间线,发现 HTMLDivElement constructor 调用了许多次,意味着内存一直被占用,无法被 GC 回收,我们知道了这些对象被分配的确切位置(createSomeNodes)。回到代码本身,探讨下如何修复内存泄露吧。
  另一个有用的特性
  在 heap allocations 的结果区域,选择 Allocation。
  这个呈现了内存分配相关的功能列表,我们立刻看到了 grow 和 createSomeNodes。当选择 grow 时,相关的 object constructor,清楚地看到 (string), HTMLDivElement 和 Text 泄露了。
  结合以上提到的工具,可以轻松找到内存泄露。
[ 责任编辑:杨瑗嘉 ]
比特网 14:14:58
从《中国互联网+指数报告(2018)》看数字经济
“互联网+”的这些新变化,你知道吗?
软件信息化周刊
比特软件信息化周刊提供以数据库、操作系统和管理软件为重点的全面软件信息化产业热点、应用方案推荐、实用技巧分享等。以最新的软件资讯,最新的软件技巧,最新的软件与服务业内动态来为IT用户找到软捷径。
商务办公周刊
比特商务周刊是一个及行业资讯、深度分析、企业导购等为一体的综合性周刊。其中,与中国计量科学研究院合力打造的比特实验室可以为商业用户提供最权威的采购指南。是企业用户不可缺少的智选周刊!
比特网络周刊向企业网管员以及网络技术和产品使用者提供关于网络产业动态、技术热点、组网、建网、网络管理、网络运维等最新技术和实用技巧,帮助网管答疑解惑,成为网管好帮手。
服务器周刊
比特服务器周刊作为比特网的重点频道之一,主要关注x86服务器,RISC架构服务器以及高性能计算机行业的产品及发展动态。通过最独到的编辑观点和业界动态分析,让您第一时间了解服务器行业的趋势。
比特存储周刊长期以来,为读者提供企业存储领域高质量的原创内容,及时、全面的资讯、技术、方案以及案例文章,力求成为业界领先的存储媒体。比特存储周刊始终致力于用户的企业信息化建设、存储业务、数据保护与容灾构建以及数据管理部署等方面服务。
比特安全周刊通过专业的信息安全内容建设,为企业级用户打造最具商业价值的信息沟通平台,并为安全厂商提供多层面、多维度的媒体宣传手段。与其他同类网站信息安全内容相比,比特安全周刊运作模式更加独立,对信息安全界的动态新闻更新更快。
新闻中心热点推荐
新闻中心以独特视角精选一周内最具影响力的行业重大事件或圈内精彩故事,为企业级用户打造重点突出,可读性强,商业价值高的信息共享平台;同时为互联网、IT业界及通信厂商提供一条精准快捷,渗透力强,覆盖面广的媒体传播途径。
云计算周刊
比特云计算周刊关注云计算产业热点技术应用与趋势发展,全方位报道云计算领域最新动态。为用户与企业架设起沟通交流平台。包括IaaS、PaaS、SaaS各种不同的服务类型以及相关的安全与管理内容介绍。
CIO俱乐部周刊
比特CIO俱乐部周刊以大量高端CIO沙龙或专题研讨会以及对明星CIO的深入采访为依托,汇聚中国500强CIO的集体智慧。旨为中国杰出的CIO提供一个良好的互融互通 、促进交流的平台,并持续提供丰富的资讯和服务,探讨信息化建设,推动中国信息化发展引领CIO未来职业发展。
IT专家新闻邮件长期以来,以定向、分众、整合的商业模式,为企业IT专业人士以及IT系统采购决策者提供高质量的原创内容,包括IT新闻、评论、专家答疑、技巧和白皮书。此外,IT专家网还为读者提供包括咨询、社区、论坛、线下会议、读者沙龙等多种服务。
X周刊是一份IT人的技术娱乐周刊,给用户实时传递I最新T资讯、IT段子、技术技巧、畅销书籍,同时用户还能参与我们推荐的互动游戏,给广大的IT技术人士忙碌工作之余带来轻松休闲一刻。  作用域
  作用域指的是变量的有效访问范围。作用域对Javascript有重要意义,了解作用域的工作原理是在性能角度和功能角度理解Javascript的关键。
  每一个JavaScript函数都被表示为对象,是一个函数实例。以下两种定义函数的方式是等价的。
var sayName = function(){
alert('hello world!');
var sayName = new Function('alert("hello world!")');
  函数对象正如其他对象那样,拥有可以被Javascript代码访问的属性,和一系列不能被Javascript代码访问,仅供JavaScript引擎使用的内部属性。其中一个内部属性是[[Scope]]。
  内部[[Scope]]属性包含一个函数被创建的作用域中对象的集合。此集合被称为函数的作用域链,它决定哪些数据可由函数访问。此函数作用域链中的每个对象被称为一个可变对象,每个可变对象都以&键值对&的形式存在。当一个函数创建后,它的作用域链被填充以对象,这些对象代表创建此函数的环境中可访问的数据。例如下面这个全局函数:
function add(num1, num2) {
var sum = num1 + num2;
  当add()函数创建后,它的内部属性指向一个作用域链,该作用域链中被填入一个单独的可变对象,这个可变对象是一个全局对象。此全局对象包含诸如窗口、浏览器和文档之类的访问接口。
  而当add函数运行时会建立一个内部对象,称作&运行期上下文&。一个运行期上下文定义了一个函数运行时的环境。对函数的每次运行而言,每个运行期上下文都是独一的,所以多次调用同一个函数就会导致多次创建运行期上下文(execution context)。当函数执行完毕,运行期上下文就被销毁。
  一个运行期上下文有它自己的作用域链,用于标识符解析。当运行期上下文被创建时,它的作用域链被初始化。包括函数的[[Scope]]属性中所包含的对象会按照它们出现在作用域链中的顺序,被复制到运行期上下文的作用域链中。这项工作一旦完成,一个被称作&激活对象&的新对象就为运行期上下文创建好了。此激活对象作为函数执行期的一个可变对象,包含访问所有局部变量,命名参数,参数集合,和this的接口。然后,此对象被推入作用域链的前端。当作用域链被销毁时,激活对象也一同销毁。下图显示了add函数运行时所对应的运行期上下文和它的作用域链。
var total = add(5, 10);
  需要记住的是两点:
[[scope]]属性是函数创建到运行时一直存在的,知道函数被销毁后占用的内存才会被释放
运行期上下文只存在于函数运行期间,函数运行结束后该对象被销毁
  闭包、内存泄露
  闭包是JavaScript最强大的一个方面,它允许函数访问局部范围之外的数据。
function assignEvents() {
var id = "xdi9592";
document.getElementById('save').onclick = function(event){
saveDocument(id);
  当assignEvents()被执行时,一个激活对象被创建,并包含了该函数作用域内所有可访问的变量和函数,其中包括id变量。它将成为运行期上下文作用域链上的第一个对象,全局对象是第二个。当闭包创建时,[[Scope]]属性包含了作用域内所有对象的集合(等于assignEvents运行期上下文的作用域链,即assignEvents的激活对象,全局对象)。
  由于闭包的[[Scope]]属性包含与运行期上下文作用域链相同的对象引用,会产生副作用。通常,一个函数的激活对象与运行期上下文一同销毁。当涉及闭包时,运行期上下文对象,以及他的作用域链被销毁,但激活对象就无法销毁,因为引用仍然存在于闭包的[[Scope]]属性中。除非手动接触所有对匿名函数的引用,等到垃圾收集器下次运行时,assignEvents的激活对象才会随着匿名函数一同被销毁。
document.getElementById('save').onclick = null;
  所以在Javascript代码中闭包与非闭包函数相比,需要更多内存开销。在大型网页应用中,这可能会导致内存泄露与难以排查的性能问题。
  随着浏览器的升级,大部分浏览器对于闭包引起的循环引用问题都能够顺利解决。但IE9之前使用非本地JavaScript对象实现DOM对象,对于Javascript对象跟DOM对象使用不同的垃圾收集器。所以闭包在IE的这些版本中发生循环引用时便会导致内存泄露。
阅读(...) 评论()410 条评论分享收藏感谢收起博客分类:
JavaScript 是一种垃圾收集式语言,这就是说,内存是根据对象的创建分配给该对象的,并会在没有对该对象的引用时由浏览器收回。JavaScript 的垃圾收集机制本身并没有问题,但浏览器在为 DOM 对象分配和恢复内存的方式上却有些出入。
Internet Explorer 和 Mozilla Firefox 均使用引用计数来为 DOM
对象处理内存。在引用计数系统,每个所引用的对象都会保留一个计数,以获悉有多少对象正在引用它。如果计数为零,该对象就会被销毁,其占用的内存也会返回
给堆。虽然这种解决方案总的来说还算有效,但在循环引用方面却存在一些盲点。
当两个对象互相引用时,就构成了循环引用,其中每个对象的引用计数值都被赋
1。在纯垃圾收集系统中,循环引用问题不大:若涉及到的两个对象中的一个对象被任何其他对象引用,那么这两个对象都将被垃圾收集。而在引用计数系统,这两
个对象都不能被销毁,原因是引用计数永远不能为零。在同时使用了垃圾收集和引用计数的混合系统中,将会发生泄漏,因为系统不能正确识别循环引用。在这种情
况下,DOM 对象和 JavaScript 对象均不能被销毁。清单 1 显示了在 JavaScript 对象和 DOM
对象间存在的一个循环引用。
&script type="text/javascript"&
document.write("circular references between JavaScript and DOM!");
window.onload = function(){
obj=document.getElementById("DivElement");
document.getElementById("DivElement").expandoProperty=
obj.bigString=new Array(1000).join(new Array(2000).join("XXXXX"));
&div id="DivElement"&Div Element&/div&
如上述清单中所示,JavaScript 对象 obj
拥有到 DOM 对象的引用,表示为 DivElement
。而 DOM 对象则有到此 JavaScript 对象的引用,由 expandoProperty
表示。可见,JavaScript 对象和 DOM 对象间就产生了一个循环引用。由于 DOM 对象是通过引用计数管理的,所以两个对象将都不能销毁。
在清单 2 中,通过调用外部函数 myFunction
创建循环引用。同样,JavaScript 对象和 DOM 对象间的循环引用也会导致内存泄漏。
&script type="text/javascript"&
document.write(" object s between JavaScript and DOM!");
function myFunction(element)
this.elementReference =
// This code forms a circular reference here
//by DOM--&JS--&DOM
element.expandoProperty =
function Leak() {
//This code will leak
new myFunction(document.getElementById("myDiv"));
&body onload="Leak()"&
&div id="myDiv"&&/div&
正如这两个代码示例所示,循环引用很容易创建。在 JavaScript 最为方便的编程结构之一:闭包中,循环引用尤其突出。
JavaScript 的过人之处在于它允许函数嵌套。一个嵌套的内部函数可以继承外部函数的参数和变量,并由该外部函数私有。清单 3 显示了内部函数的一个示例。
function parentFunction(paramA)
var a = paramA;
function childFunction()
return a + 2;
return childFunction();
JavaScript 开发人员使用内部函数来在其他函数中集成小型的实用函数。如清单 3 所示,此内部函数 childFunction
可以访问外部函数 parentFunction
的变量。当内部函数获得和使用其外部函数的变量时,就称其为一个闭包
考虑如清单 4 所示的代码片段。
&script type="text/javascript"&
document.write("Closure Demo!!");
window.onload=
closureDemoParentFunction(paramA)
var a = paramA;
return function closureDemoInnerFunction (paramB)
alert( a +" "+ paramB);
var x = closureDemoParentFunction("outer x");
x("inner x");
在上述清单中,closureDemoInnerFunction
是在父函数 closureDemoParentFunction
中定义的内部函数。当用外部的 x
对 closureDemoParentFunction
进行调用时,外部函数变量 a
就会被赋值为外部的 x
。函数会返回指向内部函数 closureDemoInnerFunction
的指针,该指针包括在变量 x
外部函数 closureDemoParentFunction
的本地变量 a
即使在外部函数返回时仍会存在。这一点不同于 C/C++ 这样的编程语言,在 C/C++ 中,一旦函数返回,本地变量也将不复存在。在 JavaScript 中,在调用 closureDemoParentFunction
的时候,带有属性 a
的范围对象将会被创建。该属性包括值 paramA
,又称为“外部 x”
。同样地,当 closureDemoParentFunction
返回时,它将会返回内部函数 closureDemoInnerFunction
,该函数包括在变量 x
由于内部函数持有到外部函数的变量的引用,所以这个带属性 a
的范围对象将不会被垃圾收集。当对具有参数值 inner x
进行调用时,即 x("inner x")
,将会弹出警告消息,表明 “outer x innerx
简要解释了 JavaScript 闭包。闭包功能非常强大,原因是它们使内部函数在外部函数返回时也仍然可以保留对此外部函数的变量的访问。不幸的是,闭包非常易于隐藏 JavaScript 对象 和 DOM 对象间的循环引用。
在清单 5 中,可以看到一个闭包,在此闭包内,JavaScript 对象(obj
)包含到 DOM 对象的引用(通过 id "element"
被引用)。而 DOM 元素则拥有到 JavaScript obj
的引用。这样建立起来的 JavaScript 对象和 DOM 对象间的循环引用将会导致内存泄漏。
&script type="text/javascript"&
document.write("Program to illustrate memory leak via closure");
window.onload=function outerFunction(){
var obj = document.getElementById("element");
obj.onclick=function innerFunction(){
alert("Hi! I will leak");
obj.bigString=new Array(1000).join(new Array(2000).join("XXXXX"));
// This is used to make the leak significant
&button id="element"&Click Me&/button&
幸好,JavaScript 中的内存泄漏是可以避免的。当确定了可导致循环引用的模式之后,正如我们在上述章节中所做的那样,您就可以开始着手应对这些模式了。这里,我们将以上述的
为例来展示三种应对已知内存泄漏的方式。
中的内存泄漏的解决方案是让此 JavaScript 对象 obj
为空,这会显式地打破此循环引用,如清单 6 所示。
&script type="text/javascript"&
document.write("Avoiding memory leak via closure by breaking the circular
reference");
window.onload=function outerFunction(){
var obj = document.getElementById("element");
obj.onclick=function innerFunction()
alert("Hi! I have avoided the leak");
// Some logic here
obj.bigString=new Array(1000).join(new Array(2000).join("XXXXX"));
obj = null
; //This breaks the circular reference
&button id="element"&"Click Here"&/button&
清单 7 是通过添加另一个闭包来避免 JavaScript 对象和 DOM 对象间的循环引用。
&script type="text/javascript"&
document.write("Avoiding a memory leak by adding another closure");
window.onload=function outerFunction(){
var anotherObj = function innerFunction()
// Some logic here
alert("Hi! I have avoided the leak");
(function anotherInnerFunction(){
document.getElementById("element");
obj.onclick=anotherObj })();
&button id="element"&"Click Here"&/button&
清单 8 则通过添加另一个函数来避免闭包本身,进而阻止了泄漏。
&script type="text/javascript"&
document.write("Avoid leaks by avoiding closures!");
window.onload=function()
var obj = document.getElementById("element");
obj.onclick = doesNotL
function doesNotLeak()
//Your Logic here
alert("Hi! I have avoided the leak");
&button id="element"&"Click Here"&/button&
本文解释了循环引用是如何导致 JavaScript 中的内存泄漏的 —— 尤其是在结合了闭包的情况下。您还了解了涉及到循环引用的一些常见内存泄漏模式以及应对这些泄漏模式的几种简单方式
(135.3 KB)
下载次数: 95
(195.4 KB)
下载次数: 88
下载次数: 94
(562.3 KB)
下载次数: 84
下载次数: 70
下载次数: 190
(216.6 KB)
下载次数: 144
下载次数: 84
浏览: 437684 次
来自: 南京
楼主写的非常详细,有点小复杂,建议用 pageoffice 插 ...
根本没效果
感谢楼主,解决了我的问题android:layerType=& ...
感谢楼主分析, 带走了
谢谢博主,问题解决了
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'

我要回帖

更多关于 js对象内存图 的文章

 

随机推荐