开发者社区> meteoric> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

IE中的内存泄露

简介: 参考文章: Winter 的《浏览器中的内存泄露》 鸟食轩的《理解并解决IE内存泄露的方式[翻译]》 IBM的《JavaScript中的内存泄露模式》   还有两篇文章: IE's memory-leak fix greatly exaggerated Memory Leakage in Internet Explorer – revisited   IE中内存泄露的几种方式: 1、循环引用(Circular References) — IE浏览器的COM组件产生的对象实例和网页脚本引擎产生的对象实例相互引用,就会造成内存泄漏。
+关注继续查看

参考文章: Winter 的《浏览器中的内存泄露》

鸟食轩的《理解并解决IE内存泄露的方式[翻译]》

IBM的《JavaScript中的内存泄露模式》

 

还有两篇文章:

IE's memory-leak fix greatly exaggerated

Memory Leakage in Internet Explorer – revisited

 

IE中内存泄露的几种方式:

1、循环引用(Circular References) — IE浏览器的COM组件产生的对象实例和网页脚本引擎产生的对象实例相互引用,就会造成内存泄漏。这也是Web页面中我们遇到的最常见和主要的泄漏方式;

2、内部函数引用(Closures) — Closures可以看成是目前引起大量问题的循环应用的一种特殊形式。由于依赖指定的关键字和语法结构,Closures调用是比较容易被我们发现的;

3、页面交叉泄漏(Cross-Page Leaks) — 页面交叉泄漏其实是一种较小的泄漏,它通常在你浏览过程中,由于内部对象薄计引起。下面我们会讨论DOM插入顺序的问题,在那个示例中你会发现只需要改动少量的代码,我们就可以避免对象薄计对对象构建带来的影响;

4、貌似泄漏(Pseudo-Leaks) — 这个不是真正的意义上的泄漏,不过如果你不了解它,你可能会在你的可用内存资源变得越来越少的时候极度郁闷。为了演示这个问题,我们将通过重写Script元素中的内容来引发大量内存的"泄漏"。

 

循环引用:

IC24175

<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 onload="SetupLeak()" onunload="BreakLeak()">
        <div id="LeakedDiv"></div>
    </body>
</html>

 

 

产生的原因:script engines handle circular references through their garbage collectors, but certain unknowns can prevent their heuristics from working properly.

 

内部函数引用:

IC24175

<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 onload="SetupLeak()" onunload="BreakLeak()">
        <div id="LeakedDiv"></div>
    </body>
</html>

 

 

页面交叉泄露:

IC24175

 

<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 onclick="LeakMemory()">Memory Leaking Insert</button>
        <button onclick="CleanMemory()">Clean Insert</button>
        <div id="hostElement"></div>
    </body>
</html>

 

 

而大多数情况下,并不会使用上面的这种方法去追加DOM节点(需要绑定事件的)

document.createElement("<div onClick='foo()'>");

通常是document.createElement,然后再使用绑定,但上面这个有事件在里面。保持自己的怀疑态度。

最后一种Pseudo-Leaks,也可以称之为伪泄露,而只有部分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 onclick="LeakMemory()">Memory Leaking Insert</button>
        <script id="hostElement">function foo() { }</script>
    </body>
</html>

 

 

所以,我觉得上面的一些例子并不是十分符合实际开发中的一些写法和规范(如监听onclck事件的方法);只是如果你不小心在代码中写下与上面相似的代码,那么它就可能已经产生内存泄露了。

 

 

 

分析一些内存泄露的例子:

<html>
<head>
<script type="text/javascript">
    function LeakMemory(){
        var parentDiv = 
             document.createElement("<div onclick='foo()'>");

        parentDiv.bigString = new Array(1000).join(
                              new Array(2000).join("XXXXX"));
    }
</script>
</head>
<body>
<input type="button" 
       value="Memory Leaking Insert" onclick="LeakMemory()" />
</body>
</html>

 

内存好像没发生什么变化?但确实发生内存泄露,为什么,因为有onclick='foo()'

何以证明?

<html>
<head>
<script type="text/javascript">
    function LeakMemory(){
        for(i = 0; i < 5000; i++){
            var parentDiv = 
               document.createElement("<div onClick='foo()'>");
        }
    }
</script>
</head>
<body>
<input type="button" 
       value="Memory Leaking Insert" onclick="LeakMemory()" />
</body>
</html>

 

<html>
<head>
<script type="text/javascript">
    function LeakMemory(){
        for(i = 0; i < 50000; i++){
            var parentDiv = 
            document.createElement("div");
        }
    }
</script>
</head>
<body>
<input type="button" 
       value="Memory Leaking Insert" onclick="LeakMemory()" />
</body>
</html>

 

比较上面的两段代码,会发现仅仅是第一段比第二段多了一个内联脚本对象(onclick=’foo()’),它没有被正确的释放。

下面的代码也会产生内存泄露的问题:

<html>
<head>
<script type="text/javascript">
    function LeakMemory(){
        var parentDiv = document.createElement("div");
                          parentDiv.onclick=function(){
            foo();
        };

        parentDiv.bigString = 
          new Array(1000).join(new Array(2000).join("XXXXX"));
    }
</script>
</head>
<body>
<input type="button" 
       value="Memory Leaking Insert" onclick="LeakMemory()" />
</body>
</html>

 

因为onclick后面的function () {}能对parentDiv进行引用

更多循环引用的例子,如下图:

<html>
<head>
<script type="text/javascript">
    var myGlobalObject;

    function SetupLeak(){
        //Here a reference created from the JS World 

        //to the DOM world.

        myGlobalObject=document.getElementById("LeakedDiv");

        //Here DOM refers back to JS World; 

        //hence a circular reference.

        //The memory will leak if not handled properly.

        document.getElementById("LeakedDiv").expandoProperty=
                                               myGlobalObject;
    }
</script>
</head>
<body onload="SetupLeak()">
<div id="LeakedDiv"></div>
</body>
</html>

 

 

IC24175

 

<html>
<head>
<script type="text/javascript">
window.onload=function(){
    // obj will be gc'ed as soon as 

    // it goes out of scope therefore no leak.

    var obj = document.getElementById("element");
    
    // this creates a closure over "element"

    // and will leak if not handled properly.

    obj.onclick=function(evt){
        ... logic ...
    };
};
</script>
</head>
<body>
<div id="element"></div>
</body>
</html>

 

 

IC24175 

 

改为下面的写法就不会产生内存泄露了

<html>
<head>
<script type="text/javascript">
window.onload=function(){
    // obj will be gc'ed as soon as 

    // it goes out of scope therefore no leak.

    var obj = document.getElementById("element");
    obj.onclick=element_click;
};

//HTML DOM object "element" refers to this function

//externally

function element_click(evt){
    ... logic ...
}
</script>
</head>
<body>
<div id="element"></div>
</body>
</html>

 

 

IC24175

 

 

 

 

虽然IE有这么多的问题,但还是有工具可以检测你写的代码是否存在内存泄露,对于代码量少、复杂度并不高的可以使用sIEve,大项目中使用它想跟踪产生内存泄露的代码则比较困难了。好在还有一个工具:Javascript Leaks Detector

JLD的强大之处在于能够模拟IE6和IE7的GC情况,和真实的回收情况。这样可以做一个比较。

msdn上官方Blog地址:blogs.msdn.com/b/gpde/archive/2009/08/03/javascript-memory-leak-detector-v2.aspx

下载地址:http://joinmicrosofteurope.com/files/IEJSLeaksDetector2.0.1.1.zip

 

 

关于 Javascript Closures 可以点击这里查看

jibbering.com/faq/notes/closures/ (英文原文)

www.cn-cuckoo.com/2007/08/01/understand-javascript-closures-72.html 中文译文

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
autojs之内存泄露
内存泄漏的概念
195 0
Java中的内存泄露的几种可能
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34173549/article/details/81057259 Java内存泄漏引起的原因:   内存泄漏是指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成内存空间的浪费称为内存泄漏。
731 0
内存分页
        虚拟存储器的思想是程序、数据和堆栈的大小都有可能超过物理内存大小,由操作系统把当前使用的放在内存,而不需要的放在磁盘。  而绝大部分操作系统使用的虚拟存储器技术就是分页技术。
1262 0
内存泄露工具
http://www.porcupine.org/forensics/forensic-discovery/ http://sourceforge.
1025 0
+关注
422
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载