JavaScript内存泄漏

本文翻译自IBM的”Memory leak patterns in JavaScript”。翻译内容仅供参考,部分内容被我稍微“篡改”了,不当的地方还请大家及时指正。原文可以看这里:
https://www.ibm.com/developerworks/web/library/wa-memleak/?S_TACT=105AGX52&S_CMP=cn-a-wa


JavaScript是一种强大的脚本语言,用于向Web页面添加动态内容。这对日常任务(如密码验证和创建动态菜单组件)尤为有益。虽然JavaScript易于学习和写入,但在某些浏览器中容易出现内存泄漏。在这篇文章中,我们将解释什么导致JavaScript的内存泄漏,展示一些需要注意的常见内存泄漏模式,并向您展示如何解决它们。

请注意,本文假设您熟悉使用JavaScript和DOM元素来开发Web应用程序。本文对于使用JavaScript进行Web应用程序开发的开发人员将是最有用的。它也可以为支持浏览器客户端的Web应用程序或任何对浏览器有疑问的人员提供参考。

JavaScript中的内存泄漏

JavaScript是一种垃圾回收语言,这意味着对象在创建时被分配内存,并且在对象的引用为0时被浏览器回收。虽然JavaScript的垃圾收集机制没有任何问题,但是与有些浏览器处理DOM对象的内存分配和内存恢复的方式不同。

Internet Explorer和Mozilla Firefox这两个浏览器是使用引用计数来处理DOM对象的内存的。在引用计数系统中,每个对象的引用计数等于其它引用该对象的对象个数(注:不知道怎么理解对不对…)。如果计数变为零,那么对象被破坏,内存返回到堆中(注:即引用计数为0,对象的内存就被回收)。虽然这种解决方案通常非常有效,但是当其涉及循环引用时具有盲点。

循环引用有什么问题?

当两个对象彼此相互引用时,就会形成循环引用,给每个对象增加1个引用计数。在一个纯垃圾回收系统中,循环引用不是问题:如果所涉及的对象都不再被其他任何对象所引用,那么就对这些对象进行垃圾回收。然而,在引用计数系统中,由于引用计数从不达到零,所以不能破坏对象。在使用垃圾回收和引用计数的混合系统中,由于系统无法识别循环引用而发生泄漏。在这种情况下,DOM对象和JavaScript对象都不会被破坏。清单1显示了JavaScript对象和DOM对象之间的循环引用。

Listing 1. 一个循环引用导致的内存泄漏

<html>
    <body>
    <script type="text/javascript">
    document.write("Circular references between JavaScript and DOM!");
    var obj;
    window.onload = function(){
    obj=document.getElementById("DivElement");
            document.getElementById("DivElement").expandoProperty=obj;
            obj.bigString=new Array(1000).join(new Array(2000).join("XXXXX"));
            };
    </script>
    <div id="DivElement">Div Element</div>
    </body>
    </html>

如上所示,JavaScript对象obj具有对由DivElement表示的DOM对象的引用。而DOM对象又通过expandoProperty引用JavaScript对象。 JavaScript对象和DOM对象之间存在循环引用。因为DOM对象是通过引用计数来管理的,所以对象都不会被破坏。

另一种内存泄漏模式

在清单2中,调用外部函数myFunction导致了一个循环引用。 JavaScript对象和DOM对象之间的循环引用再次将导致内存泄漏。

Listing 2. 调用外部函数导致的循环引用

<html>
<head>
<script type="text/javascript">
document.write("Circular references between JavaScript and DOM!");
function myFunction(element)
{
    this.elementReference = element;
    // This code forms a circular reference here
    //by DOM-->JS-->DOM
    element.expandoProperty = this;
}
function Leak() {
    //This code will leak
    new myFunction(document.getElementById("myDiv"));
}
</script>
</head>
<body onload="Leak()">
<div id="myDiv"></div>
</body>
</html>

正如这两个代码示例所示,循环引用很容易创建。它们也倾向于在JavaScript最方便的编程结构之一中出现:闭包。

JavaScript中的闭包

JavaScript的优点之一是它允许函数嵌套在其他函数中。嵌套或内部函数可以继承其外部函数的参数和变量,并且对该函数是私有的。清单3是一个内部函数的例子。

Listing 3. 一个内部函数

function parentFunction(paramA)
{
        var a = paramA;
        function childFunction()
        {
        return a + 2;
        }
        return childFunction();
}

JavaScript开发人员使用内部函数在其他函数中集成小型实用程序函数。如清单3所示,内部函数childFunction可以访问外部parentFunction的变量。当内部函数获取并使用对其外部函数变量的访问时,被称为闭包。

学习闭包

考虑清单4所示的代码片段。

<html>
<body>
<script type="text/javascript">
document.write("Closure Demo!!");
window.onload=
function  closureDemoParentFunction(paramA)
{
    var a = paramA;
    return function closureDemoInnerFunction (paramB)
    {
            alert( a +" "+ paramB);
    };
};
var x = closureDemoParentFunction("outer x");
x("inner x");
</script>
</body>
</html>

在上面的列表中,ClosDemoInnerFunction是在父函数closureDemoParentFunction中定义的内部函数。当使用外部x的参数调用closureDemoParentFunction时,外部函数的变量a被赋值为外部x。该函数返回一个指向内部函数closureDemoInnerFunction的指针,该函数包含在变量x中。

重要的是要注意外部函数closureDemoParentFunction的局部变量a即使在外部函数返回结果后也会存在。这与C / C ++等编程语言不同,一旦函数返回,局部变量就不再存在了。在JavaScript中,调用closureDemoParentFunction时,将创建一个具有属性a的作用域对象。此属性包含paramA的值,也称为“外部x”。类似地,当closureDemoParentFunction返回时,它将返回包含在变量x中的内部函数closureDemoInnerFunction。

未完待译…….

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值