IE的JScript解析器可能有Memory Leak

    在家过完了年,感觉精神很爽。明天大家可能都就上班了,新的一年开始了,希望大家新年新气象。而其我已经发现一点点新年的灵感了哦~,我原来发现IE中的JScript解释器可能有的Memory Leak的问题,可老是早不到出问题的代码,今天一下就逮到了一个可以复现的case~~

    想当初我做那个无刷新的JScript TreeView控件时,硬是被折磨坏了。IE进程IEXPLORE.EXE的内存使用量常常串到200M左右,搞得我的JScript代码运行狂慢,有的时候展开一个Node需要好几秒钟@_@。

    复现这个Bug的代码如下:
<html>
<head>
    
<title>IE Memory Leak Bug</title>
</head>
<body>
<button onclick="GenerateObjects(this)">Append Elements</button>
<div id="container"></div>
    
<script language="Javascript">
function GenerateObjects(elmt)
{
    
var room = document.getElementById('container');
    
for ( var i=0 ; i < 1000 ; ++i )
    
{
         
var obj = new TestObject('__Object__' + i);
         room.appendChild(obj.Render(document));
    }
 
}


function TestObject(name)
{
     
this.m_Name = name;
     
this.m_Description = '';
     
this.m_Element = null;
     
     
this.toString = function()
     
{
         
return '[class TestObject]'; 
     }

}


function TestObject.prototype.Render(doc)
{
     
var span = doc.createElement('SPAN');
    *span.Object 
= this;
    *
this.m_Element = span;
     
     span.Name 
= this.m_Description;
     span.innerText 
= this.m_Name;
     span.style.display 
= 'block';
     
return span;
}

</script>
</body>
</html>

    运行上面的代码,不断的点击"Generate Elements"按钮,IEXPORE.EXE的内存使用量(PM+VM)持续的上涨。这时不管你点"Refresh"还点"Go"按钮,IE的内存使用量始终不会减少。除非关掉IE在重新打开,内存才能被释放。

    问题出在上面示例代码中打"*"的那两句话上,只要注释掉任意一句,Refresh页面后,IEXPORE.EXE的内存使用量就会降到刚打开IE是那个水平上。

    有兴趣欢迎您测试一下这个例子,看看是不是会Memory Leak?

    相关文章:
        ·继续来研究JScript解析引擎的GC问题

posted on 2005-02-15 23:49 birdshome 阅读(5203) 评论(13) 编辑 收藏

评论

#1楼  回复 引用   

估计由于互相引用导致引用链有环,解释器无法判断是否为无用对象所至,应该算是ml bug,这种情况是可以感知的
2005-02-16 01:43 | 问题男

#2楼  回复 引用 查看   

这个和变成语言采用的垃圾回收算法/机制有关的,JScript采用的还是那个引用计数的算法,这个算法就是有这个天生的缺陷。现在比较成熟的变成语言一般都是采用无用标记回收算法,java和.net用的都是标记回收算法。
2005-02-16 10:22 | Laser.NET      

#3楼  回复 引用   

实际上这个bug可能比想象的还让人郁闷。

关于脚本引擎垃圾回收的问题,还不是我这个示例所关心的,因为对于IE来说,在我Refresh或Go了以后,整个页面的域都变了,而这时居然还不能释放在先前所分配的内存,真是让人抓狂。这个时候browser其实不需要care任何的引用,因为都refresh了,只需要release所有先前分配的内存就行了。

而关于引用链有环和垃圾回收机制,在同一个域中的长时间持续运行中来讨论内存使用量比较有意义。
2005-02-16 10:55 | birdshome

#4楼  回复 引用   

试看下面的例子,则可以理解为何不能一概而论的释放先前的对象了:
======= page1.htm =====================================
<html>
<body>
<button onclick="GenerateObjects(this)">Append Elements</button>
<script language="Javascript">

function GenerateObjects(elmt)
{
var obj = new TestObject('__Object__');
w = window.open("page2.htm");
w.SetObject(obj);
obj.m_Name = "modified";
}

function TestObject(name)
{
this.m_Name = name;
}

</script>
</body>
</html>
======= page1.htm end ==================================

======= page2.htm =====================================
<html>
<body>
<button onclick="Check()">Check Elements</button>
<script language="Javascript">
var obj = null;

function Check()
{
alert(obj.m_Name);
}

function SetObject(o)
{
obj = o;
}
</script>
</body>
</html>
======= page2.htm end ==================================

页面间共享对象是被ie所允许的,为了在关闭page1的情况下page2中依旧能有效的使用共享对象,ie采用了有选择的销毁page1产生的对象的策略是可以被理解的,当然也可以采用统统销毁的策略,不过这么一来有可能引发新的问题

回到实际问题,楼主您所示例子的问题出在脚本引擎应该感知对象持有者形成环、而环上对象都是无用对象的情况,在这种情况下不能正确释放内存明显是垃圾回收算法的一个bug
2005-02-16 15:16 | 问题男

#5楼  回复 引用 查看   

瓶颈在于你的For循环,很早以前遇到过你说的问题,办法也很简单,不要用对象创建和AppendChild的方法,而使用拼字符串直接赋值给InnerText 的方法会快很多。

而且你的这个例子在不同配置的机器上可能情况不同,有的配置的机器不会出现这个情况。老外早就讨论过这个问题,其实这和我们之前的讨论面向对象和非面向对象一样。技术的许多东西在于你一线间的把握和感觉,你如果一定要把它说成Bug,那也行,就问题本身而言提这个问题有些耍宝:)

W3C DOM vs. innerHTML
http://www.quirksmode.org/dom/innerhtml.html

innerHTML VS DOM.
http://www.developer-x.com/content/innerhtml/

ccBoy
2005-02-16 15:35 | ccBoy      

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

你说的对。而且只要我在页面unload的时候能把那个环状引用链表拆开,Refresh后EXPROE.EXE就能释放内存。
<body onunload="ReleaseElements()">
...
<script language="javascript">
function ReleaseElements()
{
    
var room = document.getElementById('container');
    
var spans = room.all.tags('SPAN');
    
for ( var i=0 ; i < spans.length ; ++i )
    
{
        spans[i].Object 
= null;
    }

}
    
</script>
不过这样的效率比较差,特别是生成的Elements较多的时候:(
 
@ccBoy
非常感谢您提供的资料,可是我的开发框架不太能用得上innerText或innerHTML这种模式。因为对于复杂的JS控件,我希望能把UI Element和Logic Object分开,以便于开发和维护。关于那个for只是为了让内存消耗明显些,之所以写出这个:
  span.Object = this;
  
this.m_Element = span;
这样的环状引用,也就是为了建立它们之间的双向联系而已。
 
现在我希望能有更好的办法来破除IE对环状引用链不能识别的问题,否则逻辑和UI之间不能双向引用还是比较闹心的。
2005-02-16 16:36 | birdshome      

#7楼  回复 引用   

这也是没有办法的,谁让引擎的实现基础是com呢

建议,需要建立双向联系时,m_Element之类的成员不存储对象的引用,而存储对象的name或者id之类的间接标示。这么做会令使用时有一个根据标示查找对象的过程,不过应用场合多半不是计算密集型的吧,相信牺牲的效率也不会有多少
2005-02-16 18:06 | 问题男

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

这样就回到我原来介绍的那个无限级Menu中实现逻辑对象和UI元素间关联的方法上去了,真是鱼和熊掌不能兼得啊:(
2005-02-16 18:18 | birdshome      

#9楼  回复 引用   

微软的解释:
http://www.microsoft.com/china/msdn/library/webservices/WebApp/mac0301WebQA.mspx

------------------------------------------------------------------------

问:我有一些关于 JScript® 中垃圾回收的问题。我认为在 Microsoft® Internet Explorer 6.0 中有内存泄漏,但在另一方面,它可能是循环引用问题。在我的代码中,我假设“new Object()”属于 JScript,而“document.createElement”属于 Internet Explorer DOM,因此 JScript 中的垃圾回收器无法释放 DOM 元素。这正确吗?避免这一问题的最佳方法是什么呢?

当您将代码加载到 Internet Explorer 6.0 中并单击“Refresh”时,您可以在“Task Manager”中观察到内存增加:

<html>
<body>
<script type="text/JScript">
for (i=0; i<1000; i++) { // this loop enforces the effect
var model = new Object();
var element = document.createElement("<br>");
model.myElement = element;
element.myModel = model;
model = null;
element = null;
}
</script>
</body>
</html>

答:这不是内存泄漏。您正在页面上创建新文本一千次!这会使页面变大。如果您创建了许多无法获得也无法释放的对象,那才是内存泄漏。在这里,您将创建许多元素,Internet Explorer 需要保存它们以正确呈现页面。Internet Explorer 并不知道您以后不会运行操纵您刚刚创建的所有这些对象的脚本。

当页面消失时(当您浏览完,离开浏览器时)会释放内存。它不会泄漏。当销毁页面时,会中断循环引用。

2005-04-24 09:43 | 皮皮猪

#10楼  回复 引用   

@皮皮猪
微软的解释简直扯淡,我认为解释的人根本没有执行上面那段示例代码。
"您正在页面上创建新文本一千次!这会使页面变大。如果您创建了许多无法获得也无法释放的对象,那才是内存泄漏。"回答问题的人没有理解反复refresh IE内存消耗只增不减,而且即使导航到了别的domain的页面里面后,内存好事不会释放的,已经达到了:"无法获得也无法释放"的境地了!
"当页面消失时(当您浏览完,离开浏览器时)会释放内存。"如果他只是指的关闭浏览器,按么我真的就无话可说了,否则就是windows的bug了。
现在的问题就是,销毁页面(刷新或挑转,反正就是不关闭浏览器)不能释放内存。而且问题比想象的严重。
2005-04-24 11:45 | birdshome

#11楼  回复 引用   

这种问题,只好问Microsoft,IE不释放内存的问题,可能是缓存,也可能是内存汇漏,真不好说。
2005-09-23 12:20 | chen[未注册用户]

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

@chen
对于页面的缓存,如果缓到domain都变了还在缓,我认为就是bug了。
2005-12-07 14:20 | birdshome      

导航

公告

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

搜索

 

常用链接

我的标签

随笔分类(337)

文章分类(147)

相册

Ex-Colleagues

常用链接

兄弟情深

积分与排名

  • 积分 - 3145044
  • 排名 - 6

最新评论

阅读排行榜

推荐排行榜