用户操作
[即时聊天] [发私信] [加为好友]
寒冬
最近评论
dead_of_winter:prototype只是特定时代的东西 几个极其稀有的“高手”把自己的公共代码分享出来给大家用 根本说不上是框架
prototype只用了一行代码 就能让我们这些程序员彻底对它绝望:
RegExp.prototype.match = RegExp.prototype.test;
mackyliu:显然选用框架的人大多数只是应用者,对于许多真正的开发者来说,可能更多的只是选用各个框架里比较优秀的一小段代码而已,prototype的确"污染了"许多对象,但使用者似乎也根本不需要去考虑对象的实质应用处理过程,有好的类来替代一个相对复杂的处理过程,无疑是菜鸟们的一大福音。然而对于我们搞程序的人来说,可能更多的会去关注一个框架的扩展性
dead_of_winter:hoho 不是这样的 QQ上详细说吧:)
BlueDestiny:循环引用是三者之间(或三者以上)的相互引用,只用切断其一就可以了
<html>
<head>
<script language="JScript">

var myGlobalObject;

function SetupLeak()
{
// First……
dead_of_winter:我不怀疑prototype作者的水平 不过它写得实在太随意了 不像是写框架的态度
还有啊 pt该补习下OO基础知识了 Insertion这种东西最好就别出现了- -#
YUI有多好先不说 起码这些基本的地方做得不错
文章分类
收藏
    相册
    贴图
    ACM
    哈尔滨工程大学ACM
    .net framework3.0
    中国科技大学ACM
    俄罗斯乌拉尔大学ACM......
    北京大学ACM
    吉林大学ACM(故障?)
    同济大学ACM
    哈尔滨工业大学ACM
    四川大学ACM
    天津大学ACM
    暨南大学珠海学院ACM
    杭州电子科技大学ACM
    汕头大学ACM
    浙江大学ACM
    CSDN BLOGS
    50米深蓝
    chenhu_doc
    一刀流
    小三
    搜索如风
    程化
    雁南飞
    泡的bbs
    无忧脚本
    资源
    WindowsAPI参考
    wpf(RSS)
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 浏览器中的内存泄露收藏

    新一篇: 浏览器中的内存泄露(续) | 旧一篇: MSDN CSS参考

    首先说说什么是内存泄露,在一个进程中,如果某一块内存无法访问,且直到进程结束为止也无法释放,那么就发生了内存泄露。通常这种情况发生在C++之类的手动管理内存的语言编写的程序中,程序员忘记delete或者free会导致内存泄露。本文主要讨论的是浏览器中的内存泄露问题,也就是说,javascript程序导致的内存泄露。

    目前为止最权威的关于浏览器内存泄露的文章应该是以下2篇 分别来自微软的Understanding and Solving Internet Explorer Leak Patterns(中文版)和来自IBM的Memory leak patterns in JavaScript(中文版)
    但是这2篇对js语言的认识不够深入,所以讨论的内存泄露问题和解决方案都存在一些偏差。更重要的是,他们太老了,没有介绍IE7的内存泄露新模式。希望本文下面的部分能让读到此文的人更加清晰的认识内存泄露问题。

    1.javascript对象的基础知识
    (1)创建对象
     js中创建对象的方式非常自由,通常有这样几种:直接量、new表达式、内置函数、函数调用。后面是几个例子:
     直接量:{"a":10,"b":30}
     new表达式:var o=new cls();
     内置函数:var e=document.createElement("div");var a=new ActiveXObject("XML2.0.XMLHTTP");
     函数调用:function f(){};f();
    (2)特殊的对象——作用域对象
      值得一提的是函数调用也会创建对象
      function f(){
       var a=10;
       var b=20;
      }
      尽管按照语言标准无法以任何方式访问,但是f函数在每次执行时都会创建一个有属性a和b的对象,这被称为作用域对象。而js将维护一个被称为scope chain的链表,它是一条由当前可访问的所有作用域对象组成的链表。因为js的作用域规则是定义时的作用域,所以每个函数对象被创建时都会以一个属性[[scope]]保存它的外部作用域链。

      特别地,在FireFox中,允许用__parent__访问[[scope]]属性所属的函数

      关于js的更多,可以去查阅js标准文档ECMA262(HTML版),这里无法完整地介绍js的对象机制。

    2.内存泄露的原因

    作为一门垃圾回收的语言(注意不是垃圾语言),内存泄露的原因只有一个:引擎的bug(本小节用于休闲、调节气氛)

    3.内存泄露的方式

    目前发现的可能导致内存泄露的代码有三种:

    • 循环引用
    • 自动类型装箱转换
    • 某些DOM操作

    下面具体的来说说内存是如何泄露的

    循环引用:这种方式存在于IE6和FF2中(FF3未做测试),当出现了一个含有DOM对象的循环引用时,就会发生内存泄露。

    什么是循环引用?首先搞清楚什么是引用,一个对象A的属性被赋值为另一个对象B时,则可以称A引用了B。假如B也引用了A,那么A和B之间构成了循环引用。同样道理 如果能找到A引用B B引用C C又引用A这样一组饮用关系,那么这三个对象构成了循环引用。当一个对象引用自己时,它自己形成了循环引用。注意,在js中变量永远是对象的属性,它可以指向对象,但决不是对象本身。

    循环引用很常见,而且通常是无害的,但如果循环引用中包含DOM对象或者ActiveX对象,那么就会发生内存泄露。例子:

    var a=document.createElement("div");
    var b=new Object();
    a.b
    =b;
    b.a
    =a;

    很多情况下循环引用不是这样的明显,下面就是著名的闭包(closure)造成内存泄露的例子,每执行一次函数A()都会产生内存泄露。试试看,根据前面讲的scope对象的知识,能不能找出循环引用?

    function A(){
        
    var a=document.createElement("div");
        a.onclick
    =function(){
            alert(
    "hi");
        }

    }


    A();

    OK, 让我们来看看。假设A()执行时创建的作用域对象叫做ScopeA 找到以下引用关系
    ScopeA引用DOM对象document.createElement("div");
    DOM对象document.createElement("div");引用函数function(){alert("hi")}
    函数function(){alert("hi")}引用ScopeA

    这样就很清楚了,所谓closure泄露,只不过是几个js特殊对象的循环引用而已。

    自动类型装箱转换:这种泄露存在于ie6 ie7中。这是极其匪夷所思的一个bug,看下面代码

    var s="lalalalala";
    alert(s.length);

    这段代码怎么了?看看吧,"lalalalala"已经泄露了。关键问题出在s.length上,我们知道js的类型中,string并非对象,但可以对它使用.运算符,为什么呢?因为js的默认类型转换机制,允许js在遇到.运算符时自动将string转换为object型中对应的String对象。而这个转换成的临时对象100%会泄露(汗一下)。

    某些DOM操作也可能导致泄露 这些恶心的bug只存在于ie系列中。在ie7中 因为试图fix循环引用bug而让情况变得更糟,以至于我对写这一段种满了恐惧。

    从ie6谈起,下面是微软的例子,

    <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>

    看看结果吧,LeakMemory造成了内存泄露,而CleanMemory没有,循环引用了么?仔细看看没有。那么是什么问题呢?MS的解释是"插入顺序不对",必须先将父级元素appendChild。这听起来有些模糊,这里给出一个比较恰当的等价描述:永远不要使用DOM节点树之外元素的appendChild方法

    接下来是ie7和ie8 beta 1中运行这段程序,看到什么?没看错吧,2个都泄露了!别急,刷新一下页面就好了。为什么呢?ie7改变了DOM元素的回收方式:在离开页面时回收DOM树上的所有元素,所以ie7下的内存管理非常简单:在所有的页面中只要挂在DOM树上的元素,就不会泄露,没挂在DOM树上,肯定泄露。所以,ie7中记住一条原则:在离开页面之前把所有创建的DOM元素挂到DOM树上。

    接下来谈谈ie7的这个设计吧,坦白的说,这种做法纯粹是偷懒的垃圾做法。动态垃圾回收不是保证所有内存都在离开页面时收回,而是要保证内存的充分利用,运行时不回收,等到离开时回收有什么用?这只是名义上的避免泄露,其实是完全的泄露。况且还没有回收DOM节点树之外的元素。

     4.内存泄露的解决方案

    内存泄露怎么办?真的以后不用闭包了么?没法封装控件了?这样做还不如要了js程序员的命,嘿嘿。

    事实上,通过一些很简单的小技巧,可以巧妙的绕开这些危险的bug。

    to be continued......

    coming soon:

    • 显式类型转换
    • 避免事件导致的循环引用
    • 不影响返回值地打破循环引用
    • 延迟appendChild
    • 代理DOM对象

     

    发表于 @ 2008年04月10日 14:26:00|评论(loading...)|编辑

    新一篇: 浏览器中的内存泄露(续) | 旧一篇: MSDN CSS参考

    评论

    #BlueDestiny 发表于2008-06-27 23:49:35  IP: 220.181.34.*
    循环引用.加一句话
    如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收, 所以要切断第三者~
    #dead_of_winter 发表于2008-06-28 09:36:43  IP: 218.7.43.*
    不是啊 如果是普通对象当然两个互相引用的对象也会被回收 不过如果其中之一是DOM对象就不行了
    内存泄露就是这个的问题嘛
    #BlueDestiny 发表于2008-06-28 11:26:57  IP: 220.181.34.*
    循环引用是三者之间(或三者以上)的相互引用,只用切断其一就可以了
    <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>
    以上是document.getElementById("LeakedDiv").expandoProperty->myGlobal->document.getElementById("LeakedDiv")三个相互引用,切断其一就可以了呀
    #dead_of_winter 发表于2008-06-29 00:06:01  IP: 218.7.43.*
    hoho 不是这样的 QQ上详细说吧:)
    发表评论  


    当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
    Csdn Blog version 3.1a
    Copyright © 寒冬