继续来研究JScript解析引擎的GC问题

    昨天发现了一个可以引起IE的JScript解析引擎发生Memory Leak的bug,及其引起该bug的代码。后来问题男Laser.NET两位网友给出了很多很有意义的讨论,当然ccBoy网友也给了不少建议,不过ccBoy却更关心innerHTML和appendChild的效率,对ML问题一带而过,好像觉得那根本不是什么大不了得问题

    结果我在google里搜了搜,中文论坛和网站里关于JScript GC的文章,几乎清一色全都是从MSDN上转来的一个半截文章,并且转来转去连个翻译版都没有。中文名叫"JS中关于对内存的释放问题[待续]",原文来自MSDN中:"WEB Q&A"的第三个问题。

    关于JScript脚本引擎的GC的原理和问题,下面这篇文章给予了详细的解释"How Do The Script Garbage Collectors Work?"

    JScript and VBScript both are automatic storage languages.  Unlike, say, C++, the script developer does not have to worry about explicitly allocating and freeing each chunk of memory used by the program.  The internal device in the engine which takes care of this task for the developer is called the garbage collector. 

    Interestingly enough though, JScript and VBScript have completely different garbage collectors.  Occasionally people ask me how the garbage collectors work and what the differences are.

    JScript uses a nongenerational mark-and-sweep garbage collector.  It works like this:

  • Every variable which is "in scope" is called a "scavenger".  A scavenger may refer to a number, an object, a string, whatever.  We maintain a list of scavengers -- variables are moved on to the scav list when they come into scope and off the scav list when they go out of scope.
  • Every now and then the garbage collector runs.   First it puts a "mark" on every object, variable, string, etc – all the memory tracked by the GC.  (JScript uses the VARIANT data structure internally and there are plenty of extra unused bits in that structure, so we just set one of them.)
  • Second, it clears the mark on the scavengers and the transitive closure of scavenger references.  So if a scavenger object references a nonscavenger object then we clear the bits on the nonscavenger, and on everything that it refers to.  (I am using the word "closure" in a different sense than in my earlier post.)
  • At this point we know that all the memory still marked is allocated memory which cannot be reached by any path from any in-scope variable.  All of those objects are instructed to tear themselves down, which destroys any circular references.

    Actually it is a little more complex than that, as we must worry about details like "what if freeing an item causes a message loop to run, which handles an event, which calls back into the script, which runs code, which triggers another garbage collection?"  But those are just implementation details. (Incidentally, every JScript engine running on the same thread shares a GC, which complicates the story even further...)

    You'll note that I hand-waved a bit there when I said "every now and then..."  Actually what we do is keep track of the number of strings, objects and array slots allocated.  We check the current tallies at the beginning of each statement, and when the numbers exceed certain thresholds we trigger a collection.

    The benefits of this approach are numerous, but the principle benefit is that circular references are not leaked unless the circular reference involves an object not owned by JScript. 

    However, there are some down sides as well.  Performance is potentially not good on large-working-set applications -- if you have an app where there are lots of long-term things in memory and lots of short-term objects being created and destroyed then the GC will run often and will have to walk the same network of long-term objects over and over again.  That's not fast.

    The opposite problem is that perhaps a GC will not run when you want one to.  If you say "blah = null" then the memory owned by blah will not be released until the GC releases it. If blah is the sole remaining reference to a huge array or network of objects, you might want it to go away as soon as possible. Now, you can force the JScript garbage collector to run with the CollectGarbage() method, but I don't recommend it.  The whole point of JScript having a GC is that you don't need to worry about object lifetime.  If you do worry about it then you're probably using the wrong tool for the job! 

    VBScript on the other hand, has a much simpler stack-based garbage collector.  Scavengers are added to a stack when they come into scope, removed when they go out of scope, and any time an object is discarded it is immediately freed. 

    You might wonder why we didn't put a mark-and-sweep GC into VBScript.  There are two reasons.  First, VBScript did not have classes until version 5, but JScript had objects from day one; VBScript did not need a complex GC because there was no way to get circular references in the first place!  Second, VBScript is supposed to be like VB6 where possible, and VB6 does not have a mark-n-sweep collector either.

    The VBScript approach pretty much has the opposite pros and cons.  It is fast, simple and predictable, but circular references of VBScript objects are not broken until the engine itself is shut down.

    The CLR GC is also mark-n-sweep but it is generational – the more collections an object survives, the less often it is checked for life.  This dramatically improves performance for large-working-set applications. Of course, the CLR GC was designed for industrial-grade applications, the JScript GC was designed for simple little web pages.

    What happens when you have a web page, ASP page or WSH script with both VBScript and JScript?  JScript and VBScript know nothing about each others garbage collection semantics.  A VBScript program which gets a reference to a JScript object just sees another COM object.  The same for a VBScript object passed to JScript.  A circular reference between VBScript and JScript objects would not be broken and the memory would leak (until the engines were shut down).  A noncircular reference will be freed when the object in question goes out of scope in both language (and the JS GC runs.) 

    上文中红色的代码解释了为什么我的昨天文章里的那个双向引用会产生Memory Leak的问题,因为语句:span.Object = this;和this.m_Element = span;中的span来自DHMTL对象树,而this(TestObject类的一个实例)来自JScript脚本引擎,它俩在不同的scope里,从而不能被JScript引擎中的GC机制自动回收。在昨天的文章中,问题男说道过JS的GC可能会对circular reference的情况晕菜,对于昨天我那个示例来说这个说法是正确的。不过从上文中看来并不是十分的严密,JS并不会对普通的circular reference晕菜的,只是对垮scope的reference会使其GC实效。

    昨天的文章中Laser.NET说道.NET和Java中的GC使用的是标记回收(mark-and-sweep)算法,上文中也作了解释,JScript也是使用的mark-and-sweep算法来进行GC的,只是它们在实现上的复杂度大不相同,JScript的GC是轻量级的,本身就是为Web这种轻量编程开发使用而简化实现的。

    上文的回复也挺有意思的,其中有来自developer-x.com的Tim Scarfe的抱怨,和他对Erik Arvidsson的评述,让人对Erik Arvidsson再次心生敬意。谁是Erik?! 看看这个

    BTW: KB中也还说道过一个JScript的GC bug,叫:JScript Garbage Collector Is in Inconsistent State When Many Cross-Thread Calls Are Made。不过这个bug主要影响IE5.0、IE5.01及Windows Script Engine 5.5,并且已经fixed了。

posted on 2005-02-16 23:45 birdshome 阅读(9262) 评论(24) 编辑 收藏

评论

#1楼  回复 引用   

仔细阅读了,好文

这个问题仍旧该算是个bug,对于不同scope的对象,回收时,放一块遍历并感知环的出现是理所当然的,不知ms何故遗漏了,呵呵
2005-02-17 01:51 | 问题男

#2楼  回复 引用   

拙译一篇,以和楼主

How Do The Script Garbage Collectors Work?

JScript and VBScript both are automatic storage languages. Unlike, say, C++, the script developer does not have to worry about explicitly allocating and freeing each chunk of memory used by the program. The internal device in the engine which takes care of this task for the developer is called the garbage collector.
JScript和VBScript都是自动管理存储空间的语言。不像c++之类的,脚本开发者无须担心内存的分配和释放问题。脚本引擎内部有被称作“garbage collector”(下文简称gc)的机制专门负责内存管理的工作。

Interestingly enough though, JScript and VBScript have completely different garbage collectors. Occasionally people ask me how the garbage collectors work and what the differences are.
相当有趣的是,JScript和VBScript有着完全不同的gc,但是很少有人问道“到底他们如何不同呢?”

JScript uses a nongenerational mark-and-sweep garbage collector. It works like this:
JScript使用称作“nongenerational mark-and-sweep gc”的收集机制,他们这样工作:

Every variable which is "in scope" is called a "scavenger". A scavenger may refer to a number, an object, a string, whatever. We maintain a list of scavengers -- variables are moved on to the scav list when they come into scope and off the scav list when they go out of scope.
每个“scope”内变量被叫做“scavenger”,一个scavenger指向一个数、对象、字符串或任何其他什么,我们维护一条scavenger的链表,当变量进入scope时它被加入这条链表,移出scope时被从链表剔除。

Every now and then the garbage collector runs. First it puts a "mark" on every object, variable, string, etc – all the memory tracked by the GC. (JScript uses the VARIANT data structure internally and there are plenty of extra unused bits in that structure, so we just set one of them.)
每次当gc被激活时,首先,他为每个变量打上一个标记(JScript内部使用VARIANT结构,这个结构有很多不使用的域可被用于存放标签)。

Second, it clears the mark on the scavengers and the transitive closure of scavenger references. So if a scavenger object references a nonscavenger object then we clear the bits on the nonscavenger, and on everything that it refers to. (I am using the word "closure" in a different sense than in my earlier post.)
然后,gc清除scavenger的标记以及scavenger引用变量的闭包集合的成员的标记,所以如果一个scavenger对象指向另一个非scavenger对象,那么gc也清除那个对象的标记,并重复这一过程(这里“closure”的意思和我先前的一篇文章中的不同)。

At this point we know that all the memory still marked is allocated memory which cannot be reached by any path from any in-scope variable. All of those objects are instructed to tear themselves down, which destroys any circular references.
到这里,我们发现所有scope内不再使用到的变量仍旧被标记着,所有这些无用对象将被释放,同样也包括循环引用的。
2005-02-17 14:17 | 问题男

#3楼  回复 引用   

Actually it is a little more complex than that, as we must worry about details like "what if freeing an item causes a message loop to run, which handles an event, which calls back into the script, which runs code, which triggers another garbage collection?" But those are just implementation details. (Incidentally, every JScript engine running on the same thread shares a GC, which complicates the story even further...)
实际上,实现时我们必须考虑一些诸如“释放一个什么对象会令消息循环运行,谁处理事件,谁将回调脚本代码,谁运行代码,谁触发另一个gc”的问题,因此会比这个算法描述的稍稍复杂一点(另外,运行在同一个线程内的JScript引擎都共享同一个gc,将来这些也许会更复杂)。

You'll note that I hand-waved a bit there when I said "every now and then..." Actually what we do is keep track of the number of strings, objects and array slots allocated. We check the current tallies at the beginning of each statement, and when the numbers exceed certain thresholds we trigger a collection.
可能你注意到上述第二步有些含糊,实际上我们实际做的是跟踪内存分配的量,并在每次执行脚本语句开始查看当前这个量是否超过了限制,如果是,那么gc将被触发。

The benefits of this approach are numerous, but the principle benefit is that circular references are not leaked unless the circular reference involves an object not owned by JScript.
采用这个方法有很多好处,其根本益处在于不会令循环引用的变量永远得不到释放,除非环上一个变量不属于JScript。

However, there are some down sides as well. Performance is potentially not good on large-working-set applications -- if you have an app where there are lots of long-term things in memory and lots of short-term objects being created and destroyed then the GC will run often and will have to walk the same network of long-term objects over and over again. That's not fast.
然而,一些不利因素也存在,它将在“large-working-set”应用中诱发潜在的性能瓶颈,说白了就是,如果你有一个应用需要使用很多“long-term”变量(他们生存周期很长),而又有很多“short-term”对象,他们创建释放很频繁,这时候,gc也会随之频繁被触发,而长期对象和短期对象存在于同一张引用图上,gc一遍一遍的遍历,每次都要检查很长一段时间内不可能销毁的长期对象,这明显会很慢。

The opposite problem is that perhaps a GC will not run when you want one to. If you say "blah = null" then the memory owned by blah will not be released until the GC releases it. If blah is the sole remaining reference to a huge array or network of objects, you might want it to go away as soon as possible. Now, you can force the JScript garbage collector to run with the CollectGarbage() method, but I don't recommend it. The whole point of JScript having a GC is that you don't need to worry about object lifetime. If you do worry about it then you're probably using the wrong tool for the job!
相对的,令人困扰的是有时你希望gc被激活时它恰恰达不到被激活的条件。如果你想通过类似“blah = null”的代码释放blah原本占用的空间是徒劳的,直到gc释放它之前它将一直存在。如果blah是唯一的一个大的复杂数据的拥有者,你可能想在你需要释放它的时候立即进行释放动作。现在,你可以随时使用CollectGarbage()函数强制JScript激活gc,虽然我不建议你这么做。JScript最大的优势在于可以让你不必担心内存的管理问题,如果你必须为此担心,那么可能JScript并不是你完成当前工作最恰当的工具。

VBScript on the other hand, has a much simpler stack-based garbage collector. Scavengers are added to a stack when they come into scope, removed when they go out of scope, and any time an object is discarded it is immediately freed.
另一方面,VBScript有着更加简单的“stack-based”型gc,scavenger在进入scope时被压入栈,在离开scope时被弹出栈,只要一个对象没用了它就被立即释放。
2005-02-17 14:17 | 问题男

#4楼  回复 引用   

You might wonder why we didn't put a mark-and-sweep GC into VBScript. There are two reasons. First, VBScript did not have classes until version 5, but JScript had objects from day one; VBScript did not need a complex GC because there was no way to get circular references in the first place! Second, VBScript is supposed to be like VB6 where possible, and VB6 does not have a mark-n-sweep collector either.
你可能对VBScript没有使用“mark-and-sweep”机制感到好奇。这里有两个原因,首先,在第5版之前VBScript没有class这一概念,但从诞生起JScript就有objcet,VBScript因没法产生变量的循环引用而无需复杂的gc。其次,呵呵,VBScript处处力图模仿VB6,而VB6的gc没有采用mark-n-sweep机制,当然,VBScript也不用。

The VBScript approach pretty much has the opposite pros and cons. It is fast, simple and predictable, but circular references of VBScript objects are not broken until the engine itself is shut down.
VBScript的实现方法有利有弊,它快速、简单、行为可预计,但是循环引用的对象将持续存在直到解释器运行终止才被释放。

The CLR GC is also mark-n-sweep but it is generational – the more collections an object survives, the less often it is checked for life. This dramatically improves performance for large-working-set applications. Of course, the CLR GC was designed for industrial-grade applications, the JScript GC was designed for simple little web pages.
CLR的gc也采用了mark-n-sweep方式,但它是“generational”(的),即“幸免”次数越多的对象,今后被检查的次数越少(也就是在多次收集后,很长时间都“有效”的对象,在今后的检查中将被gc有意识的略过)。这种策略将有效的改善large-working-set应用的性能。当然,CLR是为industrial-grade应用而生的,而JScript是为小型web应用而设计的,两者的gc显然没有可比性。

What happens when you have a web page, ASP page or WSH script with both VBScript and JScript? JScript and VBScript know nothing about each others garbage collection semantics. A VBScript program which gets a reference to a JScript object just sees another COM object. The same for a VBScript object passed to JScript. A circular reference between VBScript and JScript objects would not be broken and the memory would leak (until the engines were shut down). A noncircular reference will be freed when the object in question goes out of scope in both language (and the JS GC runs.)
当页面中既存在JScript又有VBScript时将发什么什么呢?他们将无法意识到对方的存在而各行其是。一个VBScript对象仅仅把得到一个JScript对象看作一个COM对象而已,JScript也是一样。两种脚本对象间互相引用形成的环将不会被gc切断,并且导致内存泄漏(直到解释器关闭)。非环状引用的对象将被正常释放。
2005-02-17 14:18 | 问题男

#5楼  回复 引用   

非常棒的翻译:)
2005-02-17 14:25 | birdshome

#6楼  回复 引用 查看   

赞!两位牛人!pf!!:)
2005-02-17 16:17 | Laser.NET      

#7楼  回复 引用 查看   

我的观点还是如此。我感觉内存泄露来源你的代码,而不是GC或IE.

你应该为span.Object = this; 负责,this 代表什么?
也许是一个DHMTL对象树,也许是整个的Page/Document对象,也许代表背后生成的就是一个For循环产生的COM对象数组.也许是你之前代码中的HTC控件(我看到代码中有许多this而猜测之前可能这是htc控件中的一段代码)

对于问题男所说的,"对于不同scope的对象,应该检查环",但根据上面的文章,说明JScrtip引擎的最后方法可能就是判断变量是否离开scope,企图摧毁所有和它相连的指针和内存。但是也许它不保证摧毁之外已经启动的许多COM组件的内存、指针和引用。所以我相信环检查是会做的,但是释放之外的内存可能是无法做到的。

"当页面中既存在JScript又有VBScript时将发什么什么呢?他们将无法意识到对方的存在而各行其是。一个VBScript对象仅仅把得到一个JScript对象看作一个COM对象而已,JScript也是一样。两种脚本对象间互相引用形成的环将不会被gc切断,并且导致内存泄漏(直到解释器关闭)。非环状引用的对象将被正常释放。"
这句话很好,我想JS和IE HTML的COM对象也是如此.所以最后不得不关闭些什么:)


再则,之前我们项目遇到类似的情况,比如For循环的数量加大到5000或10000,用Add的方式去影响 HTMLDOM树+XML会发现内存一样会消耗的很多(256M的机器可能会白屏2分钟,512的05分钟,如果是1G,可能20秒就OK,当然可能还会和CPU/网络速度等相关)。项目还是要做,所以最后将HTC控件改成了innerHTML的方式,提高了速度,所以我昨天更多的希望从效率的角度来避免完全面向对象的方式带来的低效。心底里不想这么理论:) ,说道底,我们只是一个IE的用户,又不是开发人员.

当然IE会有内存泄露,但通过GC的方度来解释,似乎有些牵强,GC可以减少内存泄露,了解GC对程序员来说非常重要,但不是说有了GC,咱们的代码无论怎么写都不会造成泄露。
如果说内存泄露,至少分IE的内存泄露和你代码造成的内存泄露。
如果问题男的假设成立,是因为没有检测环引用,那么算是程序员的疏忽,内存泄露是IE的,而你说的情况更多的是自己代码造成的内存泄露

也许我是一个实用主义者,对于我来说讨论内存泄露最后最可能是下结论为一个Bug(没有下文或等待补丁),而讨论面向对象/效率会锻炼我(或我的团队)写出更有效率的代码,学到更多。

ccBoy
2005-02-17 17:55 | ccBoy      

#8楼[楼主]  回复 引用 查看   

@ccBoy
    非常高兴看到你的经验之谈,毕竟我做大规模JS开发的时间还很短,不到半年。之所以这么郁闷,并不是我不接受你说的innerHTML代替appendChild这种高效的DHTML的操作方式,而是不同的是实现方式对项目后期的开发效率,以及JS开发框架的形成有很大的影响,借用你说的:技术的许多东西在于你一线间的把握和感觉。昨天问题男说了一下使用HTML元素的ID作为唯一标示来建立Html Element和JScript Object之间的联系就不会有我那个问题了,并说了一下副作用就是消耗一点点效率,可是效率却也是我非常care的,看看这篇文章你就知道我对效率是多么的锱铢必较了。
    从上面的讨论我们可以看出,JScript引擎已经把HTML DOM对象当作com(反正就是不能探测的scope)一类来看了,我当然也必须想办法在我的JS组件的解构时把跨scope的环链表解开。至于你说的这个问题该追究IE、追究JScript的GC还是追究Developer?我觉得并不重要。你是用innerHTML避开了跨scope的circular reference引用环问题,我手动解环不也是一样的解决的问题了吗?毕竟JScript规范理又没有禁止span.Object = this;不是
    再说远一点,在.NET中CLR也是不能释放COM对象和Unmanaged的资源,我们同样可以选者只是用Managed的资源,把GC看成完全透明;或者使用Unmanaged的资源,最后自己dispose它嘛。
2005-02-17 18:36 | birdshome      

#9楼[楼主]  回复 引用 查看   

再啰嗦一句,我咋就对appendChild操作DHTML那么感冒?还非要相互引用啊?其实这确实是鱼和熊掌不能兼得问题。
2005-02-17 18:39 | birdshome      

#10楼  回复 引用   

脚本方面的垃圾回收

MICROSOFT的这个的确做得不好。主要问题是因为JScript与DHTML都是基于COM的。COM在这个的处理上有缺陷。

我通常遇到的问题是HTML ELEMENT事件的关联,如果没有释放,那么内存就不会释放。如果主动detachEvent,那样就没有问题了。

//在网上看到的,附在这里供大家参考讨论
2005-02-17 21:41 | birdshome

#11楼  回复 引用   

谈谈我遇到的问题,重复点击菜单打开功能页面,功能页面装载js,功能页面的表格绑定了一些函数,js的代码较长3000行左右,
我这么说不知道能不能表达清楚,就是多次点击菜单,内存持续增加,没有止境,
环境win2000,
不知道有没有什么办法解决?
2006-01-24 13:03 | ligang[未注册用户]

#12楼[楼主]  回复 引用 查看   

@ligang
可以在窗口退出前手动的清除一些引用和邦定,比如xxxObj.abc = null。
从我的实验来看,可以有一些效果,内存涨得会慢些,不过很多时候还是不能完全制止内存使用量的增长:(
2006-01-24 16:52 | birdshome      

#13楼  回复 引用   

看了讨论很受启发,最近正在用MS的IE Webcontrols里的treeview控件,发现点节点的时候客户端的cpu利用率非常高,大概60%到100%。并且长时间使用的情况下,内存使用量会非常大。检查了一下,发现可能是htc里面的代码呢问题,不知道有什么办法解决
2006-03-20 14:30 | Treeview[未注册用户]

#14楼  回复 引用   

是啊是啊,也遇到了这样的问题呢,

最小化时内存使用量会马上减少,
尝试在page的onunload事件调用 CollectGarbage() 好像却没有多大作用,
郁闷。
2006-03-23 13:55 | 毛尖儿[未注册用户]

#15楼[楼主]  回复 引用 查看   

@毛尖儿
调用CollectGarbage()方法,只是改变了内存被自动回收的时间,而不能改变脚本引擎回收内存的算法。并且作为自动的GC的过程,最好是不要手动干预它,也就是说显示的调用CollectGarbage()方法,可能引起更加严重的莫名问题。
2006-03-23 14:08 | birdshome      

#16楼  回复 引用   

@birdshome



可是
“可以在窗口退出前手动的清除一些引用和邦定”
这种方法也尝试了,也没用
:(
2006-03-23 14:24 | 毛尖儿[未注册用户]

#17楼  回复 引用   



可是也尝试用
“可以在窗口退出前手动的清除一些引用和邦定”
同样没有效果。
:(
2006-03-23 14:28 | 毛尖儿[未注册用户]

#18楼[楼主]  回复 引用 查看   

@毛尖儿
目前IE6+sp1在内存回收上的表现,确实已经超过了我们能忍耐的限度。包括微软最新的解释,也是非常非常非常的牵强,我们现在只能尽量避免使用微软不推荐的编程方式,并祈祷微软快些推出IE和脚本引擎内存管理的补丁:<
2006-03-23 14:31 | birdshome      

#19楼  回复 引用   

-----
"可以在窗口退出前手动的清除一些引用和邦定”

------

我的实践证明,使用这个,效果很不明显。
2006-03-27 17:46 | workingbird[未注册用户]

#20楼[楼主]  回复 引用 查看   

@workingbird
你也泄了啊?谁的IE不泄,我的IE天天泄,ft

btw: 就目前来看,微软也是建议这么做的
2006-03-27 17:53 | birdshome      

#21楼  回复 引用   

微软所谓的解释确实牵强
不光是JS会导致内存泄漏,JS的内存泄漏还是可以避免的,写程序的人可以感知,可以避免
但是ie内部的DOM对象的不得释放就非外界能够干预了

事实上 在浏览器里面 浏览about:blank页面
不停的刷新
内存也还是会上涨的

这个确实非常郁闷

2006-04-15 20:17 | qianyi[未注册用户]

#22楼  回复 引用   

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/IETechCol/dnwebgen/ie_leak_patterns.asp

找了一片关于ie内存泄漏 的几种情况
看起来比较费劲
JScript确实有些地方让人匪夷所思

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

这段代码就很搞笑,

The cause of the leak in this pattern is based on COM reference counting. The script engine objects will hold a reference to the DOM element and will be waiting for any outstanding references to be removed before cleaning up and releasing the DOM element pointer. In our case we have two references on the script engine object: the script engine scope, and the DOM element expando property. While terminating the script engine will release the first reference, the DOM element reference will never be released because it is waiting on the script engine object to release it! You might think it would be easy to detect this scenario and fix the problem, but in practice the basic case presented is only the tip of the iceberg. You could have circular references at the end of a 30 object chain and those would be much harder to detect.

他说导致泄漏的原因是因为com的引用计数机制,script对象会保持对com对象的引用. 直到外部对该script对象的引用被释放掉,该script对象才会释放com对象的引用,并且调用com对象的release减去该引用计数,但是该script对象被com对象所引用,又引用了com对象,所以两者都得不到释放.当script解析器被终止的时候(^_^,我就不知道怎么终止了)会释放掉script对象的引用,这样script对象也就消失了,但是该script对象没有释放对com对象的引用(作为com对象来说,他只看引用计数),这样这个com对象就不会被释放掉.


下面的代码,是另一种循环引用
<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>


function ClickEventHandler(){}是AttachEvents()的内部函数对象,调用这个函数的时候element.attachEvent("onclick", ClickEventHandler); ClickEventHandler会指向element,element的onclick又指向ClickEventHandler,这样就形成了另外一种互相引用.


呵呵 所以避免大规模的内存损失,最好用script对象引用com的时候得非常小心,避免两者互相引用,这点birdshome说得很清楚.
这也是主要的JScript引起的内存泄漏的方式(互相引用,最后不能release com对象,导致com对象得不到释放)


但是我得浏览器就是浏览about:blank,刷新的时候内存也会以4K的幅度向上长,这个让我很头疼.

不知道各位大虾有什么见解!



2006-04-16 23:35 | qianyi[未注册用户]

#23楼  回复 引用   

多谢楼主的分享,我在研究 json,需要动态创建 <script> 所以也遇上了这个内存不释放的问题。看来只能是用 innerHTML 了。
2007-03-05 12:16 | 梅花雪[未注册用户]

导航

公告

  原创技术文章和心得,转载必须注明来源"博客园"!
  贴子以"现状"提供,且没有任何担保,同时也没有授予任何权利。
昵称:birdshome
园龄:7年10个月
荣誉:推荐博客
粉丝:73
关注:3

搜索

 

常用链接

我的标签

随笔分类(337)

文章分类(147)

相册

Ex-Colleagues

常用链接

兄弟情深

积分与排名

  • 积分 - 3145044
  • 排名 - 6

最新评论

阅读排行榜

推荐排行榜