zoukankan      html  css  js  c++  java
  • IE中的内存泄露

    参考文章: 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 中文译文

  • 相关阅读:
    CodeSmith功能和技巧收集
    简繁转换js兼容各种浏览器
    40 个轻量级 JavaScript 库
    AJAX处理Session
    对项目管理的几点认识(转)
    extjs
    数据采集需要的方法
    JavaScript 浮动定位提示效果
    一个类别表设计
    ExtJS 源码剖析 —— Ext类
  • 原文地址:https://www.cnblogs.com/meteoric_cry/p/1825951.html
Copyright © 2011-2022 走看看