JavaScript之详述闭包导致的内存泄露
一、内存泄露
1. 定义:一块被分配的内存既不能使用,也不能回收。从而影响性能,甚至导致程序崩溃。
2. 起因:JavaScript的垃圾自动回收机制会按一定的策略找出那些不再继续使用的变量,释放其占有的内存。然而由于一些原因导致在这种机制下内存管理器不能正确解读JavaScript变量的生命周期,从而没有释放其内存,而也没有再被使用。
循环引用是导致以上情况的主要原因之一。
二、循环引用
1. 定义:两个对象的互相引用。
IE和Mozilla Firefox均使用引用计数的策略对变量和内存进行管理,当出现循环引用时,各对象的引用数至少为1,在不进行手动设置的情况下,将无法被释放。
闭包实际上非常容易造成JavaScript对象和DOM对象的隐蔽循环引用。来看看下面的例子:
function example(){
var element =document.getElementByID("div1"); //①
element.onclick = function() {
alert("This is a leak!"); //②
}
}
以上函数example() 中用匿名函数创建了一个闭包。
第①句:JavaScript对象element引用了一个DOM对象(其id为“div1”); JS(element) ----> DOM(div1)
第②句:该DOM对象的onclick属性引用了匿名函数闭包,而闭包可以引用外部函数example() 的整个活动对象,包括elemnt ; DOM(div1.onclick) ---->JS(element)
由此形成了JavaScript对象和DOM对象的隐蔽循环引用。
2.解决方法:
常用的解决方法就是在JavaScript代码段运行完之时将形成循环引用的JavaScript对象手动设置为空,切断引用。
修改的例子如下:
function example(){
var element =document.getElementByID("div1"); //①
element.onclick = function() {
alert("This is a leak!"); //②
}
element = null; //添加的语句
}
一、内存泄露
1. 定义:一块被分配的内存既不能使用,也不能回收。从而影响性能,甚至导致程序崩溃。
2. 起因:JavaScript的垃圾自动回收机制会按一定的策略找出那些不再继续使用的变量,释放其占有的内存。然而由于一些原因导致在这种机制下内存管理器不能正确解读JavaScript变量的生命周期,从而没有释放其内存,而也没有再被使用。
循环引用是导致以上情况的主要原因之一。
二、循环引用
1. 定义:两个对象的互相引用。
IE和Mozilla Firefox均使用引用计数的策略对变量和内存进行管理,当出现循环引用时,各对象的引用数至少为1,在不进行手动设置的情况下,将无法被释放。
闭包实际上非常容易造成JavaScript对象和DOM对象的隐蔽循环引用。来看看下面的例子:
function example(){
var element =document.getElementByID("div1"); //①
element.onclick = function() {
alert("This is a leak!"); //②
}
}
以上函数example() 中用匿名函数创建了一个闭包。
第①句:JavaScript对象element引用了一个DOM对象(其id为“div1”); JS(element) ----> DOM(div1)
第②句:该DOM对象的onclick属性引用了匿名函数闭包,而闭包可以引用外部函数example() 的整个活动对象,包括elemnt ; DOM(div1.onclick) ---->JS(element)
由此形成了JavaScript对象和DOM对象的隐蔽循环引用。
2.解决方法:
常用的解决方法就是在JavaScript代码段运行完之时将形成循环引用的JavaScript对象手动设置为空,切断引用。
修改的例子如下:
function example(){
var element =document.getElementByID("div1"); //①
element.onclick = function() {
alert("This is a leak!"); //②
}
element = null; //添加的语句
}
如果您知道内存泄漏的起因,那么在 JavaScript 中进行相应的防范就应该相当容易。在这篇文章中,作者 Kiran Sundar 和 Abhijeet Bhattacharya 将带您亲历 JavaScript 中的循环引用的全部基本知识,向您介绍为何它们会在某些浏览器中产生问题,尤其是在结合了闭包的情况下。在了解了您应该引起注意的常见内存泄漏模式之后,您还将学到应对这些泄漏的诸多方法。
JavaScript 是用来向 Web 页面添加动态内容的一种功能强大的脚本语言。它尤其特别有助于一些日常任务,比如验证密码和创建动态菜单组件。JavaScript 易学易用,但却很容易在某些浏览器中引起内存的泄漏。在这个介绍性的文章中,我们解释了 JavaScript 中的泄漏由何引起,展示了常见的内存泄漏模式,并介绍了如何应对它们。
注意本文假设您已经非常熟悉使用 JavaScript 和 DOM 元素来开发 Web 应用程序。本文尤其适合使用 JavaScript 进行 Web 应用程序开发的开发人员,也可供有兴趣创建 Web 应用程序的客户提供浏览器支持以及负责浏览器故障排除的人员参考。
JavaScript 中的内存泄漏
JavaScript 是一种垃圾收集式语言,这就是说,内存是根据对象的创建分配给该对象的,并会在没有对该对象的引用时由浏览器收回。JavaScript 的垃圾收集机制本身并没有问题,但浏览器在为 DOM 对象分配和恢复内存的方式上却有些出入。
Internet Explorer 和 Mozilla Firefox 均使用引用计数来为 DOM 对象处理内存。在引用计数系统,每个所引用的对象都会保留一个计数,以获悉有多少对象正在引用它。如果计数为零,该对象就会被销毁,其占用的内存也会返回给堆。虽然这种解决方案总的来说还算有效,但在循环引用方面却存在一些盲点。
循环引用的问题何在?
当两个对象互相引用时,就构成了循环引用,其中每个对象的引用计数值都被赋 1。在纯垃圾收集系统中,循环引用问题不大:若涉及到的两个对象中的一个对象被任何其他对象引用,那么这两个对象都将被垃圾收集。而在引用计数系统,这两个对象都不能被销毁,原因是引用计数永远不能为零。在同时使用了垃圾收集和引用计数的混合系统中,将会发生泄漏,因为系统不能正确识别循环引用。在这种情况下,DOM 对象和 JavaScript 对象均不能被销毁。清单 1 显示了在 JavaScript 对象和 DOM 对象间存在的一个循环引用。
清单 1. 循环引用导致了内存泄漏
<html>
<body>
Div Element
</body>
</html>
如上述清单中所示,JavaScript 对象 obj 拥有到 DOM 对象的引用,表示为 DivElement。而 DOM 对象则有到此 JavaScript 对象的引用,由 expandoProperty 表示。可见,JavaScript 对象和 DOM 对象间就产生了一个循环引用。由于 DOM 对象是通过引用计数管理的,所以两个对象将都不能销毁。
另一种内存泄漏模式
在清单 2 中,通过调用外部函数 myFunction 创建循环引用。同样,JavaScript 对象和 DOM 对象间的循环引用也会导致内存泄漏。
清单 2. 由外部函数调用引起的内存泄漏
<html>
<head>
</head>
<body onload="Leak()">
</body>
</html>
正如这两个代码示例所示,循环引用很容易创建。在 JavaScript 最为方便的编程结构之一:闭包中,循环引用尤其突出。
JavaScript 中的闭包
JavaScript 的过人之处在于它允许函数嵌套。一个嵌套的内部函数可以继承外部函数的参数和变量,并由该外部函数私有。清单 3 显示了内部函数的一个示例。
清单 3. 一个内部函数
function parentFunction(paramA)
{
var a = paramA;
function childFunction()
{
return a + 2;
}
return childFunction();
}
JavaScript 开发人员使用内部函数来在其他函数中集成小型的实用函数。如清单 3 所示,此内部函数 childFunction 可以访问外部函数 parentFunction 的变量。当内部函数获得和使用其外部函数的变量时,就称其为一个闭包。
了解闭包
考虑如清单 4 所示的代码片段。
清单 4. 一个简单的闭包
<html>
<body>
</body>
</html>
在上述清单中,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 进行调用时,即 x("inner x"),将会弹出警告消息,表明 “outer x innerx”。
清单 4 简要解释了 JavaScript 闭包。闭包功能非常强大,原因是它们使内部函数在外部函数返回时也仍然可以保留对此外部函数的变量的访问。不幸的是,闭包非常易于隐藏 JavaScript 对象 和 DOM 对象间的循环引用。
闭包和循环引用
在清单 5 中,可以看到一个闭包,在此闭包内,JavaScript 对象(obj)包含到 DOM 对象的引用(通过 id "element" 被引用)。而 DOM 元素则拥有到 JavaScript obj 的引用。这样建立起来的 JavaScript 对象和 DOM 对象间的循环引用将会导致内存泄漏。
清单 5. 由事件处理引起的内存泄漏模式
<html>
<body>
<button id="element">Click Me</button>
</body>
</html>
避免内存泄漏
幸好,JavaScript 中的内存泄漏是可以避免的。当确定了可导致循环引用的模式之后,正如我们在上述章节中所做的那样,您就可以开始着手应对这些模式了。这里,我们将以上述的 由事件处理引起的内存泄漏模式 为例来展示三种应对已知内存泄漏的方式。
一种应对 清单 5 中的内存泄漏的解决方案是让此 JavaScript 对象 obj 为空,这会显式地打破此循环引用,如清单 6 所示。
清单 6. 打破循环引用
<html>
<body>
<button id="element">"Click Here"</button>
</body>
</html>
清单 7 是通过添加另一个闭包来避免 JavaScript 对象和 DOM 对象间的循环引用。
清单 7. 添加另一个闭包
<html>
<body>
<button id="element">"Click Here"</button>
</body>
</html>
清单 8 则通过添加另一个函数来避免闭包本身,进而阻止了泄漏。
清单 8. 避免闭包自身
<html>
<head>
</head>
<body>
<button id="element">"Click Here"</button>
</body>
</html>
结束语
本文解释了循环引用是如何导致 JavaScript 中的内存泄漏的 —— 尤其是在结合了闭包的情况下。您还了解了涉及到循环引用的一些常见内存泄漏模式以及应对这些泄漏模式的几种简单方式。
闭包的情况下。您还了解了涉及到循环引用的一些常见内存泄漏模式以及应对这些泄漏模式的几种简
一、内存泄露
1. 定义:一块被分配的内存既不能使用,也不能回收。从而影响性能,甚至导致程序崩溃。
2. 起因:JavaScript的垃圾自动回收机制会按一定的策略找出那些不再继续使用的变量,释放其占有的内存。然而由于一些原因导致在这种机制下内存管理器不能正确解读JavaScript变量的生命周期,从而没有释放其内存,而也没有再被使用。
循环引用是导致以上情况的主要原因之一。
二、循环引用
1. 定义:两个对象的互相引用。
IE和Mozilla Firefox均使用引用计数的策略对变量和内存进行管理,当出现循环引用时,各对象的引用数至少为1,在不进行手动设置的情况下,将无法被释放。
闭包实际上非常容易造成JavaScript对象和DOM对象的隐蔽循环引用。来看看下面的例子:
function example(){ var element =document.getElementByID("div1"); //① element.onclick = function() { alert("This is a leak!"); //② }}以上函数example() 中用匿名函数创建了一个闭包。第①句:JavaScript对象element引用了一个DOM对象(其id为“div1”); JS(element) ----> DOM(div1)
第②句:该DOM对象的onclick属性引用了匿名函数闭包,而闭包可以引用外部函数example() 的整个活动对象,包括elemnt ; DOM(div1.onclick) ---->JS(element)由此形成了JavaScript对象和DOM对象的隐蔽循环引用。
2.解决方法:
常用的解决方法就是在JavaScript代码段运行完之时将形成循环引用的JavaScript对象手动设置为空,切断引用。
修改的例子如下:
function example(){ var element =document.getElementByID("div1"); //① element.onclick = function() { alert("This is a leak!"); //② } element = null; //添加的语句}
JavaScript之详述闭包导致的内存泄露的更多相关文章
- 找出并解决 JavaScript 和 Dojo 引起的浏览器内存泄露问题
简介: 如果大量使用 JavaScript 和 Ajax 技术开发 Web 2.0 应用程序,您很有可能会遇到浏览器的内存泄漏问题.如果您有一个单页应用程序或者一个页面要处理很多 UI 操作,问题可能 ...
- js闭包和ie内存泄露原理
也议 js闭包和ie内存泄露原理 可以, 但小心使用. 闭包也许是 JS 中最有用的特性了. 有一份比较好的介绍闭包原理的文档. 有一点需要牢记, 闭包保留了一个指向它封闭作用域的指针, 所以, 在给 ...
- dotnet 6 在 Win7 系统证书链错误导致 HttpWebRequest 内存泄露
本文记录我将应用迁移到 dotnet 6 之后,在 Win7 系统上,因为使用 HttpWebRequest 访问一个本地服务,此本地服务开启 https 且证书链在此 Win7 系统上错误,导致应用 ...
- JavaScript 中 4 种常见的内存泄露陷阱
了解 JavaScript 的内存泄露和解决方式! 在这篇文章中我们将要探索客户端 JavaScript 代码中常见的一些内存泄漏的情况,并且学习如何使用 Chrome 的开发工具来发现他们.读一读吧 ...
- JavaScript垃圾回收(三)——内存泄露
一.JavaScript内存监测工具 在讨论内存泄露之前,先介绍几款JavaScript内存监测工具. IE的sIEve与JSLeaksDetector(这两个可以在下面的附件中下载),firefox ...
- 也议 js闭包和ie内存泄露原理
可以, 但小心使用. 闭包也许是 JS 中最有用的特性了. 有一份比较好的介绍闭包原理的文档. 有一点需要牢记, 闭包保留了一个指向它封闭作用域的指针, 所以, 在给 DOM 元素附加闭包时, 很可能 ...
- JavaScript:4个常见的内存泄露
什么是内存泄漏 内存泄漏基本上就是不再被应用需要的内存,由于某种原因,没有被归还给操作系统或者进入可用内存池. 编程语言喜欢不同的管理内存方式.然而,一段确定的内存是否被使用是一个不可判断的问题.换句 ...
- logging 模块误用导致的内存泄露
首先介绍下怎么发现的吧, 线上的项目日志是通过 logging 模块打到 syslog 里, 跑了一段时间后发现 syslog 的 UDP 连接超过了 8W, 没错是 8 W. 主要是 logging ...
- 深度:ARC会导致的内存泄露
iOS提供了ARC功能,很大程度上简化了内存管理的代码. 但使用ARC并不代表了不会发生内存泄露,使用不当照样会发生内存泄露. 下面列举两种内存泄露的情况. 1,循环参照 A有个属性参照B,B有个属性 ...
随机推荐
- JAVA中JDK1.8的LocalDateTime日期类的操作方法
LocalDateTime与Date相互转换参考:https://www.cnblogs.com/pxblog/p/13745972.html 关键类 Instant:瞬时时间. LocalDate: ...
- 【剑指Offer】字符串的排列 解题报告(Python)
[剑指Offer]字符串的排列 解题报告(Python) 标签(空格分隔): LeetCode 题目地址:https://www.nowcoder.com/ta/coding-interviews 题 ...
- ZFNet: Visualizing and Understanding Convolutional Networks
目录 论文结构 反卷积 ZFnet的创新点主要是在信号的"恢复"上面,什么样的输入会导致类似的输出,通过这个我们可以了解神经元对输入的敏感程度,比如这个神经元对图片的某一个位置很敏 ...
- [CodeLife]记毕业后第一份工作
记毕业后第一份工作与公司 写在前面--前言 已然临近21年五月,很快又是一年毕业季了,公司里来了应届的新人,忽然才意识到自己已经不是公司年龄最小的了((笑~).依稀还记得两年前,自己也是如他们那般青涩 ...
- 使用所学Spring知识,实现简易的图书查询系统功能。实现查询全部图书。 根据书籍编号查询信息。 根据书名查询书籍信息。 根据状态查询书籍信息。
相关 知识 >>> 相关 练习 >>> 实现要求: 使用所学Spring知识,实现简易的图书查询系统功能. 要求如下: 查询全部图书. 根据书籍编号查询信息. 根据 ...
- Spring练习,使用Properties类型注入方式,注入MySQL数据库连接的基本信息,然后使用JDBC方式连接数据库,模拟执行业务代码后释放资源,最后在控制台输出打印结果。
相关 知识 >>> 相关 练习 >>> 实现要求: 使用Properties类型注入方式,注入MySQL数据库连接的基本信息,然后使用JDBC方式连接数据库,模拟执 ...
- 简单学生管理系统HTML前端页面
效果图: 实现代码: <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> ...
- Java初学者作业——编写JAVA程序,要求输入技术部门5位员工的理论成绩和实操成绩,计算并输出各位员工的最终评测成绩。
返回本章节 返回作业目录 需求说明: 某软件公司要求对技术部门的所有员工进行技能评测,技术评测分为两个部分:理论部分以及实操部分,最终评测成绩=理论成绩×0.4+实操成绩×0.6,要求输入技术部门5位 ...
- 编写Java程序,前方有 3km 的道路障碍,4 辆普通车不能通过,必须等到清障车完成作业离开后,才能继续行驶。用程序来模拟这一过程的发生
查看本章节 查看作业目录 需求说明: 前方有 3km 的道路障碍,4 辆普通车不能通过,必须等到清障车完成作业离开后,才能继续行驶.用程序来模拟这一过程的发生 实现思路: 创建清障车Wrecker类和 ...
- java 短路与(||)时报错The operator || is undefined for the argument type(s) int, boolean
今天在使用短路与时,报错The operator || is undefined for the argument type(s) int, boolean 代码如下: 最后发现是少了一个=,比较要使 ...