如何协助IE的有缺陷的垃圾回收器?

3
我有一个 JavaScript 应用程序,使用 XMLHttpRequest 来获取和解析大约 60,000 个 XML 文档。然而,IE 的内存使用量快速增加,最终程序崩溃。我怀疑这与 IE 的 JScript 垃圾回收有关。以下是我的代码的简化版本:
在代码上面,我声明了两个变量:
var xmlhttp;
var xmlDoc;

当代码开始运行时,我设置了xmlhttp的值:
xmlhttp = new XMLHttpRequest();

脚本然后进入主循环:
function loadXML() {

    xmlhttp.abort(); 
    xmlhttp.open("GET", url, false);
    xmlhttp.setRequestHeader('Content-Type', 'text/xml', 'Pragma', 'no-cache');
    xmlhttp.send("");
    while (xmlhttp.readyState != 4) { }
    xmlDoc = xmlhttp.responseXML;
    setTimeout("readXML()",0);

}


function readXML() {

    //Reads the XML.
    //If all data has been retrieved, exit loop.
    //Else, change the url and go back to loadXML()

}

谷歌浏览器可以正常运行代码,没有错误。然而,IE在循环大约2000次后就会崩溃并显示“内存不足”错误。难道垃圾回收器没有起作用吗?我能否重写我的代码以避免问题?


1
setTimeout(...,0)可能是问题,我指的是0部分。 - Liviu T.
不,问题(虽然与泄漏无关)是他传递了一个字符串...当然,他将XHR同步设置为异步处理响应的事实也是一个问题... - ThiefMaster
@Liviu 因为脚本在两个函数之间循环了很多次,所以浏览器会冻结直到代码自行终止。尽管听起来很傻,但设置一个0毫秒的超时将允许浏览器从脚本中获取控制权,避免冻结。 - Michael
@ThiefMaster,我希望能得到一些关于如何纠正我的代码的建议。我在JS方面并不是专业人士,所以并不懂所有东西! :) - Michael
@Michael:关于同步/异步,请参考@phihag的回答。对于setTimeout,您只需传递函数:setTimeout(readXML, 0) - 但是使用onreadystatechange事件的正确方式,您将不需要它。 - ThiefMaster
2个回答

3

不应该使用繁忙循环来等待XMLHttpRequest的结果。此外,没有理由将xmlhttp对象设置为公共的。相反,每次调用时都要创建一个新的对象并注册回调函数:

function loadXML() {
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open("GET", url, false);
    xmlhttp.setRequestHeader('Content-Type', 'text/xml', 'Pragma', 'no-cache');
    xmlhttp.onreadystatechange = function() {
      if (xmlhttp.readyState == 4) {
        xmlDoc = xmlhttp.responseXML;
        readXML();
      }
   };
   xmlhttp.send("");
}

我会尝试这个。它应该需要大约20分钟来运行。 - Michael
一般来说,我认为任何人都会同意崩溃比安全漏洞更好。因此,我鼓励Web开发人员尽可能加剧IE的GC问题,这样用户就会倾向于用真正的浏览器替换崩溃的IE,从而关闭所有那些敞开的安全漏洞。在OP的情况下,原始代码如此有缺陷,我会给这个答案一个+1。 - Michael Lorton
@Michael 哎呀,我忘了一件事。你不应该重复使用XMLHttpRequest对象。我已经更新了答案并附上了整个代码。它不应该需要超过几秒钟来运行。 - phihag
@phihag 感谢您的建议,现在循环大约4500次,仅使用约5000KB!我已将您添加到鸣谢部分。 - Michael

1

你应该一定要遵循phihag的建议,采用更好的方式处理xml请求并等待它们完成。

然后我建议将旧的xmlhttp对象置空,并为每个连续的请求创建一个新的对象,以便每个旧请求都可以完全释放:

你没有展示给我们如何运行相同的代码60,000次,所以我无法帮助你解决代码的细节问题,但如果xmlhttp对象本身在每个xmlhttp请求中泄漏了一些内存,那么丢弃旧对象并每次创建一个新对象可能会有所帮助。

我们也看不到你在readXML中可能泄漏的内容,或者在循环并获取下一个请求的代码中做了什么。你可能会泄漏函数闭包,可能会有循环对象引用等等...


谢谢您的帮助!我已经实现了phihag的代码和您的xmlhttp = null;建议,脚本运行得很好!我已将您加入到鸣谢名单中。 - Michael

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接