javascript内存泄露
2019独角兽企业重金招聘Python工程师标准>>>
1. 什么是内存泄露?
内存泄露是指分配给应用的内存不能被重新分配,即使在内存已经不被使用的时候。正常情况下,脚本引擎通过垃圾收集器(GC)在 DOM 元素和 event 处理器不被引用或访问的时候回收它们。但是,IE 的早些版本(IE7和之前)中内存泄露是很容易出现的,因为内存管理器不能正确理解 Javascript 生命周期而且在周期被打破(可以通过赋值为 null 实现)前不会回收内存。
2. 为什么你需要注意它?
在大型 Web 应用程序中内存泄露是一种常见的无意的编程错误。内存泄露会降低 Web 应用程序的性能,直到浪费的内存超过了系统所能分配的,应用程序将不能使用。作为一位 Web 开发者,开发一个满足功能要求的应用程序只是第一步,性能要求和 Web 应用程序的成功是同样重要的,更何况它可能会导致应用程序错误或浏览器崩溃。
3. Javascript 中出现内存泄露的主要原因是什么?
1) 循环引用
一个很简单的例子:一个 DOM 对象被一个 Javascript 对象引用,与此同时又引用同一个或其它的 Javascript 对象,这个 DOM 对象可能会引发内存泄露。这个 DOM 对象的引用将不会在脚本停止的时候被垃圾回收器回收。要想破坏循环引用,引用 DOM 元素的对象或 DOM 对象的引用需要被赋值为 null。如: IE浏览器的COM组件产生的对象实例和网页脚本引擎产生的对象实例相互引用,就会造成内存泄漏。这也是Web页面中我们遇到的最常见和主要的泄漏方式;
<html>
<head>
<script language="JScript">
var myGlobalObject;
function SetupLeak()
{
// First set up the script scope to element reference
myGlobalObject = document.getElementById("LeakedDiv");
// Next set up the element to script scope reference
document.getElementById("LeakedDiv").expandoProperty = myGlobalObject;
}
function BreakLeak()
{
document.getElementById("LeakedDiv").expandoProperty = null;
}
</script>
</head>
<body οnlοad="SetupLeak()" οnunlοad="BreakLeak()">
<div id="LeakedDiv"></div>
</body>
</html>
析:本模型中引起的泄漏问题基于COM的引用计数。脚本引擎对象会维持对DOM对象的引用,并在清理和释放DOM对象指针前等待所有引用的移除。在我们的示例中,我们的脚本引擎对象上有两个引用:脚本引擎作用域和DOM对象的expando属性。当终止脚本引擎时第一个引用会释放,DOM对象引用由于在等待脚本擎的释放而并不会被释放。你可能会认为检测并修复假设的这类问题会非常的容易,但事实上这样基本的的示例只是冰山一角。你可能会在30个对象链的末尾发生循环引用,这样的问题排查起来将会是一场噩梦。
对基于对象的JScript,一个通常用法是通过封装JScript对象来扩充DOM对象。在构建过程中,你常常会把DOM对象的引用放入JScript对象中,同时在DOM对象中也存放上对新近创建的JScript对象的引用。你的这种应用模式将非常便于两个对象之间的相互访问。
<html>
<head>
<script language="JScript">
function Encapsulator(element)
{
// Set up our element
this.elementReference = element;
// Make our circular reference
element.expandoProperty = this;
}
function SetupLeak()
{
// The leak happens all at once
new Encapsulator(document.getElementById("LeakedDiv"));
}
function BreakLeak()
{
document.getElementById("LeakedDiv").expandoProperty = null;
}
</script>
</head>
<body οnlοad="SetupLeak()" οnunlοad="BreakLeak()">
<div id="LeakedDiv"></div>
</body>
</html>
2)、内部函数引用(Closures) — Closures可以看成是目前引起大量问题的循环应用的一种特殊形式。由于依赖指定的关键字和语法结构,Closures调用是比较容易被我们发现的;
3) Javascript 闭包
因为 Javascript 范围的限制,许多实现依赖 Javascript 闭包。如果你想了解更多闭包方面的问题,请查看我的前面的文章 JavaScript Scope and Closure 。
闭包可以导致内存泄露是因为内部方法保持一个对外部方法变量的引用,所以尽管方法返回了,内部方法还可以继续访问在外部方法中定义的私有变量。对 Javascript 程序员来说最好的做法是在页面重载前断开所有的事件处理器。普通的循环引用,是两个不可探知的对象相互引用造成的,但是闭包却不同。代替直接造成引用,闭包函数则取而代之从其父函数作用域中引入信息。通常,函数的局部变量和参数只能在该被调函数自身的生命周期里使用。当存在闭包函数后,这些变量和参数的引用会和闭包函数一起存在,但由于闭包函数可以超越其父函数的生命周期而存在,所以父函数中的局部变量和参数也仍然能被访问。在下面的示例中,参数1将在函数调用终止时正常被释放。当我们加入了一个闭包函数后,一个额外的引用产生,并且这个引用在闭包函数释放前都不会被释放。如果你碰巧将闭包函数放入了事件之中,那么你不得不手动从那个事件中将其移出。如果你把闭包函数作为了一个expando属性,那么你也需要通过置null将其清除。
<html>
<head>
<script language="JScript">
function AttachEvents(element)
{
// This structure causes element to ref ClickEventHandler
element.attachEvent("onclick", ClickEventHandler);
function ClickEventHandler()
{
// This closure refs element
}
}
function SetupLeak()
{
// The leak happens all at once
AttachEvents(document.getElementById("LeakedDiv"));
}
function BreakLeak()
{
}
</script>
</head>
<body οnlοad="SetupLeak()" οnunlοad="BreakLeak()">
<div id="LeakedDiv"></div>
</body>
</html>
4) DOM 插入顺序
当 2 个不同范围的 DOM 对象附加到一起的时候,一个临时的对象会被创建。这个 DOM 对象改变范围到 document 时,那个临时对象就没用了。也就是说, DOM 对象应该按照从当前页面存在的最上面的 DOM 元素开始往下直到剩下的 DOM 元素的顺序添加,这样它们就总是有同样的范围,不会产生临时对象。如:页面交叉泄漏(Cross-Page Leaks) — 页面交叉泄漏其实是一种较小的泄漏,它通常在你浏览过程中,由于内部对象薄计引起。下面我们会讨论DOM插入顺序的问题,在那个示例中你会发现只需要改动少量的代码,我们就可以避免对象薄计对对象构建带来的影响;
<html>
<head>
<script language="JScript">
function AttachEvents(element)
{
// This structure causes element to ref ClickEventHandler
element.attachEvent("onclick", ClickEventHandler);
function ClickEventHandler()
{
// This closure refs element
}
}
function SetupLeak()
{
// The leak happens all at once
AttachEvents(document.getElementById("LeakedDiv"));
}
function BreakLeak()
{
}
</script>
</head>
<body οnlοad="SetupLeak()" οnunlοad="BreakLeak()">
<div id="LeakedDiv"></div>
</body>
</html>
<html>
<head>
<script language="JScript">
function LeakMemory()
{
var hostElement = document.getElementById("hostElement");
// Do it a lot, look at Task Manager for memory response
for(i = 0; i < 5000; i++)
{
var parentDiv =
document.createElement("<div onClick='foo()'>");
var childDiv =
document.createElement("<div onClick='foo()'>");
// This will leak a temporary object
parentDiv.appendChild(childDiv);
hostElement.appendChild(parentDiv);
hostElement.removeChild(parentDiv);
parentDiv.removeChild(childDiv);
parentDiv = null;
childDiv = null;
}
hostElement = null;
}
function CleanMemory()
{
var hostElement = document.getElementById("hostElement");
// Do it a lot, look at Task Manager for memory response
for(i = 0; i < 5000; i++)
{
var parentDiv =
document.createElement("<div onClick='foo()'>");
var childDiv =
document.createElement("<div onClick='foo()'>");
// Changing the order is important, this won't leak
hostElement.appendChild(parentDiv);
parentDiv.appendChild(childDiv);
hostElement.removeChild(parentDiv);
parentDiv.removeChild(childDiv);
parentDiv = null;
childDiv = null;
}
hostElement = null;
}
</script>
</head>
<body>
<button οnclick="LeakMemory()">Memory Leaking Insert</button>
<button οnclick="CleanMemory()">Clean Insert</button>
<div id="hostElement"></div>
</body>
</html>
4)
貌似泄漏(Pseudo-Leaks)
在大多数时候,一些APIs的实际的行为和它们预期的行为可能会导致你错误的判断内存泄漏。貌似泄漏大多数时候总是出现在同一个页面的动态脚本操作中,而在从一个页面跳转到空白页面的时候发生是非常少见的。那你怎么能象排除页面间泄漏那样来排除这个问题,并且在新任务运行中的内存使用量是否是你所期望的。我们将使用脚本文本的重写来作为一个貌似泄漏的示例。
象DOM插入顺序问题那样,这个问题也需要依赖创建临时对象来产生"泄漏"。对一个脚本元素对象内部的脚本文本一而再再而三的反复重写,慢慢地你将开始泄漏各种已关联到被覆盖内容中的脚本引擎对象。特别地,和脚本调试有关的对象被作为完全的代码对象形式保留了下来。
<html>
<head>
<script language="JScript">
function LeakMemory()
{
// Do it a lot, look at Task Manager for memory response
for(i = 0; i < 5000; i++)
{
hostElement.text = "function foo() { }";
}
}
</script>
</head>
<body>
<button οnclick="LeakMemory()">Memory Leaking Insert</button>
<script id="hostElement">function foo() { }</script>
</body>
</html>
4) 如何检测?
内存泄露对开发者来说一般很难检测,因为它们是由大量代码中的一些意外的错误引起的,但它在系统内存不足前并不影响程序的功能。这就是为什么会有人在很长时间的测试期中收集应用程序性能指标来测试性能。
最简单的检测内存泄露的方式是用任务管理器检查内存使用情况。在 Chrome 浏览器的新选项卡中打开应用并查看内存使用量是不是越来越多。还有其他的调试工具提供内存监视器,比如 Chrome 开发者工具。这是谷歌开者这网站中的堆分析的特性的教程。
转载于:https://my.oschina.net/starmier/blog/169010
javascript内存泄露相关推荐
- (转)javascript 内存泄露工具使用
javascript内存泄露的问题一直以来都不受到大家的重视,原因是对用户的影响没有太实际的表现,或许近几年内存发展迅速.脚本内存再泄露也不会有太大影响. 当然作为前端开发的同学们,就不能有这样的侥幸 ...
- JavaScript内存泄露,闭包内存泄露如何解决
转载于:JavaScript内存泄露,闭包内存泄露如何解决 - 一粒一世界 - 博客园 JavaScript 内存泄露的4种方式及如何避免 简介 什么是内存泄露? JavaScript 内存管理 Ja ...
- JavaScript 内存泄露的4种方式及如何避免
内存泄露是每个开发者最终都要面对的问题,它是许多问题的根源:反应迟缓,崩溃,高延迟,以及其他应用问题. 什么是内存泄露? 本质上,内存泄露可以定义为:应用程序不再需要占用内存的时候,由于某些原因,内存 ...
- JavaScript内存泄露的4种方式及如何避免
简介 内存泄露是每个开发者最终都要面对的问题,它是许多问题的根源:反应迟缓,崩溃,高延迟,以及其他应用问题. 什么是内存泄露? 本质上,内存泄露可以定义为:应用程序不再需要占用内存的时候,由于某些原因 ...
- 无法读取内存属于错误吗_深入了解 JavaScript 内存泄露
用户一般不会在一个 Web 页面停留比较久,即使有一点内存泄漏,重载页面内存也会跟着释放.而且浏览器也有自动回收内存的机制,所以我们前端其实并没有像 C.C++ 这类语言一样,特别关注内存泄漏的问题. ...
- 深入了解 JavaScript 内存泄露
这篇文章是针对浏览器的 JavaScript 脚本,Node.js 大同小异,这里不涉及到 Node.js 的场景.当然 Node.js 作为服务端语言,必然更关注内存泄漏的问题. 用户一般不会在一个 ...
- JavaScript:内存泄露
2019独角兽企业重金招聘Python工程师标准>>> 了解 JavaScript 应用程序中的内存泄漏 Javascript内存泄露 js内存泄露的几种情况 性能调优之Javasc ...
- JavaScript 的垃圾回收与内存泄露
2019独角兽企业重金招聘Python工程师标准>>> JavaScript采用垃圾自动回收机制,运行时环境会自动清理不再使用的内存,因此javascript无需像C++等语言一样手 ...
- 理解在javascript中的内存泄露
在处理像javascript这类的脚本语言时,很容易忽视对象,实例,字符串,数字和方法都需要分配内存.分配与重新分配都被脚本语言本身和运行时的垃圾回收器隐藏. 没有内存管理,也许你可以完成许多事情.但 ...
最新文章
- CPU与GPU的区别
- 【原创】构建高性能ASP.NET站点 开篇
- k8s 查看ip地址属于哪个pod_Kubernetes Pod 如何获取 IP 地址
- 解决在Android Studio 3.2找不到Android Device Monitor工具
- 4-1 简单输出整数 (10分)
- python面试题之如何用Python找出你目前在哪个目录?
- 【LeetCode】盛最多水的容器【双指针+贪心 寻找最大面积】
- Navicat for MySQL 64位破解版
- CANoe——CAPL
- Java面试学习资源
- userscript ajax,在Greasemonkey的userscript文本追加到一个表单时使用Ajax提交
- oracle批量修改同义词,ORACLE数据库 批量创建同义词
- word文档怎么找回误删的文件_怎样找回误删除的word文件?
- 在数据库中,视图有什么用?什么时候需要用到视图?
- basketball
- Poco::Exception. Code: 1000, e.code() = 0, e.displayText() = Host not found
- 高逼格UI-ASD(Android Support Design)
- Linux 切换到root用户
- Chrome浏览器扩展开发之自动化操作页面
- 2021年中式面点师(高级)复审模拟考试及中式面点师(高级)作业试题解析