JScript - 脚本块和内存泄漏 - 如何正确释放资源?

8
我在我的项目中的一个部分视图中放置了一些jquery选项卡。我注意到,在调试期间,通过Visual Studio的“解决方案资源管理器”,每次我点击一个新选项卡时,都会生成一个新的动态JScript脚本块。
即使我在选项卡的“show”事件中放置了“$('#mytabs .ui-tabs-hide').children().remove();”和“$(".ui-tabs-hide").empty();”,这种情况仍然会发生。脚本块包含我放置在被选项卡调用的部分视图中的JavaScript代码,因此每次我单击以前单击过的选项卡时,都会出现一个新的JScript块:很明显,这会导致稳定性或内存泄漏问题...例如,我已经注意到,加载两次选项卡后,某些计时器和绑定不能正常工作。
我不知道问题是否是由于调用包含脚本的部分视图的方式引起的。请注意我如何设置控制器操作(例如Index)。
这是我的环境:jquery 1.6.4 - jquery-ui 1.8.16 - IE 8.0.7601 我无法使用其他浏览器进行调试,因为Visual Studio似乎没有连接它们的进程,并且不显示动态数据...
控制器
以下是由选项卡调用的一个示例操作
  public ActionResult Index()
    {
         if (Request.IsAjaxRequest())
            return PartialView("_Index");

        return View(); 
    }

以下是我对视图和脚本的一些看法和代码:
_Layout.cshtml
 ....
 <div id="body">  
    @Html.Partial("_TabsMenu");
 </div>
 ....

_TabsMenu.cshtml(包含选项卡菜单的部分视图)

 <div id="menutabs" class="content-wrapper">
    <ul >
        <li>@Html.ActionLink("Home", "Index", "Home")</li>
        <li>@Html.ActionLink("Test", "Index", "Test")</li>
         ...
    </ul>
 </div>

 <script type="text/javascript">
 $(function () {
     $('#menutabs').tabs({
         cache: false,
         show: function (event, ui) {
             $('#menutabs .ui-tabs-hide').children().remove(); // the content is removed , but the script is still in memory
             $(".ui-tabs-hide").empty(); // the content is removed, but the script is still in memory
         },
         select: function (event, ui) {
             $(window).unbind(); 
         }
     });
 });

我尝试将脚本放在div id中,也许这很傻,但我想看看DOM中的脚本是否被删除了...但是没有任何效果。
Index.cshtml
  @{Html.RenderPartial("_Index");}

_Index.cshtml(包含问题重复的jscript对象的部分视图)


Note: 该文件是一个部分视图,其中包含了重复的 JavaScript 对象,用于处理问题。
   <table id="list4"></table>
   <jQuery("#list4").jqGrid({
datatype: "local",
height: 250,
colNames:['Inv No','Date', 'Client', 'Amount','Tax','Total','Notes'],
colModel:[
    {name:'id',index:'id', width:60, sorttype:"int"},
    {name:'invdate',index:'invdate', width:90, sorttype:"date"},
    {name:'name',index:'name', width:100},
    {name:'amount',index:'amount', width:80, align:"right",sorttype:"float"},
    {name:'tax',index:'tax', width:80, align:"right",sorttype:"float"},     
    {name:'total',index:'total', width:80,align:"right",sorttype:"float"},      
    {name:'note',index:'note', width:150, sortable:false}       
],
multiselect: true,
caption: "Manipulating Array Data"});
            var mydata = [
    {id:"1",invdate:"2007-10-01",name:"test",note:"note",amount:"200.00",tax:"10.00",total:"210.00"},
    {id:"2",invdate:"2007-10-02",name:"test2",note:"note2",amount:"300.00",tax:"20.00",total:"320.00"},
    {id:"3",invdate:"2007-09-01",name:"test3",note:"note3",amount:"400.00",tax:"30.00",total:"430.00"},
    {id:"4",invdate:"2007-10-04",name:"test",note:"note",amount:"200.00",tax:"10.00",total:"210.00"},
    {id:"5",invdate:"2007-10-05",name:"test2",note:"note2",amount:"300.00",tax:"20.00",total:"320.00"},
    {id:"6",invdate:"2007-09-06",name:"test3",note:"note3",amount:"400.00",tax:"30.00",total:"430.00"},
    {id:"7",invdate:"2007-10-04",name:"test",note:"note",amount:"200.00",tax:"10.00",total:"210.00"},
    {id:"8",invdate:"2007-10-03",name:"test2",note:"note2",amount:"300.00",tax:"20.00",total:"320.00"},
    {id:"9",invdate:"2007-09-01",name:"test3",note:"note3",amount:"400.00",tax:"30.00",total:"430.00"}
    ];
       for(var i=0;i<=mydata.length;i++)
    jQuery("#list4").jqGrid('addRowData',i+1,mydata[i]);

更新

JScript - 脚本块 1..N // 在调试期间,我在每个 JScript - 脚本块中看到的内容... 我正在测试 jqgrid。这是来自 Trirand 网站的演示

     <jQuery("#list4").jqGrid({
datatype: "local",
height: 250,
colNames:['Inv No','Date', 'Client', 'Amount','Tax','Total','Notes'],
colModel:[
    {name:'id',index:'id', width:60, sorttype:"int"},
    {name:'invdate',index:'invdate', width:90, sorttype:"date"},
    {name:'name',index:'name', width:100},
    {name:'amount',index:'amount', width:80, align:"right",sorttype:"float"},
    {name:'tax',index:'tax', width:80, align:"right",sorttype:"float"},     
    {name:'total',index:'total', width:80,align:"right",sorttype:"float"},      
    {name:'note',index:'note', width:150, sortable:false}       
],
multiselect: true,
caption: "Manipulating Array Data"});
            var mydata = [
    {id:"1",invdate:"2007-10-01",name:"test",note:"note",amount:"200.00",tax:"10.00",total:"210.00"},
    {id:"2",invdate:"2007-10-02",name:"test2",note:"note2",amount:"300.00",tax:"20.00",total:"320.00"},
    {id:"3",invdate:"2007-09-01",name:"test3",note:"note3",amount:"400.00",tax:"30.00",total:"430.00"},
    {id:"4",invdate:"2007-10-04",name:"test",note:"note",amount:"200.00",tax:"10.00",total:"210.00"},
    {id:"5",invdate:"2007-10-05",name:"test2",note:"note2",amount:"300.00",tax:"20.00",total:"320.00"},
    {id:"6",invdate:"2007-09-06",name:"test3",note:"note3",amount:"400.00",tax:"30.00",total:"430.00"},
    {id:"7",invdate:"2007-10-04",name:"test",note:"note",amount:"200.00",tax:"10.00",total:"210.00"},
    {id:"8",invdate:"2007-10-03",name:"test2",note:"note2",amount:"300.00",tax:"20.00",total:"320.00"},
    {id:"9",invdate:"2007-09-01",name:"test3",note:"note3",amount:"400.00",tax:"30.00",total:"430.00"}
    ];
       for(var i=0;i<=mydata.length;i++)
    jQuery("#list4").jqGrid('addRowData',i+1,mydata[i]);                

你可以在大多数现代浏览器控制台中对JavaScript进行分析,这比在IDE中进行测试要好得多。如果您的jQgrid测试直接来自演示,没有进行任何修改...我怀疑VS会给您提供错误的信息。如果是修改过的代码,您可能已经设置了某种过度递归。 - charlietfl
3个回答

8
在浏览器中解析的脚本不在DOM中,因此您无法“删除”它-变量仍然被定义,事件仍然被绑定,方法仍然存在。如果您将JavaScript放入重复加载的部分视图中,那么您将反复获得该JavaScript。
您需要做的是编写更具弹性的JavaScript。如果您将事件绑定到动态区域外的元素,请不要这样做。您将多次进行绑定。将该代码移动到只会加载一次的位置。尝试将JavaScript隔离在动态区域中,使其只处理也处于动态区域中的元素。
您还可以使用简单的if检查来防止多个定义,使用更加范围化的jQuery选择器等。
如果没有重复的块内容的详细信息,我提供的帮助就有限了。

谢谢@bhamlin!我把涉及到的JavaScript代码放在了问题里。你能帮我避免这种不良行为并隔离问题吗?每次切换标签都会在内存中加载一个新的脚本。我想避免这种情况,我认为这也可以帮助其他人。非常感谢! - Larry
1
我猜list1应该是指ticketgrid,而pager1应该是指jqpager。话虽如此,每次加载脚本块并没有什么本质的问题。如果出现奇怪的行为,可能与jqGrid的实现方式有关,这可能超出了本问题的范围。如果你只是想每次加载较少的脚本,你可以将代码块作为函数放在Index.cshtml中,并让_Index.cshtml调用它,传递要操作的列表和分页器元素。 - bhamlin
你是对的。那是一个错误。我更正了名称并提供了另一个使用静态数据的示例,这样每个人都可以测试和重现它。 我知道每次加载脚本都没有问题,但问题就像我说的那样,脚本从未被卸载。此外,所有代码都在这里,每个人都可以测试和重现行为。 jqGrid 的实现非常简单,所以不幸的是这不是问题的关键,我想。 - Larry
问题就像我说的那样,脚本永远不会被卸载。脚本无法被卸载,这是不存在的事情。 - bhamlin
脚本无法卸载,这对我来说太奇怪了。难道没有人实现一种方法来释放未使用脚本的内存吗?不存在设置到期、GC或其他方式的方法吗? 请问您能否给我一个例子,以便重复使用同一脚本并避免在每个选项卡更改时重新加载它?非常感谢您的帮助。 - Larry

3
我觉得你的问题是每次加载一个选项卡时,其中所有的脚本也会被加载。就我所知,你无法改变这种情况,但是请记住,你可以使用服务器端代码生成这些脚本,因此ASP.NET必须像它们都不同一样处理它们(因为它们实际上是不同的)。
然而,JavaScript是垃圾回收的,因此我认为当您不进行调试时,您看到的那些脚本实际上并没有占用用户的内存。无论是否已进行垃圾回收,调试器始终会显示所有已加载的内容(尽管我实际上没有测试过)。
如果您担心内存问题,只需确保不声明全局函数和变量,这些变量永远不会被垃圾回收(特别是在重新加载的范围内)。为此,只需将它们放在一个匿名函数中,以便在执行后立即进行垃圾回收:

谢谢你,Eduardo。你对垃圾回收的假设让我感到放心,虽然没有绝对的确定性,但你能给我一些测试建议吗? - Larry
我在JavaScript内存泄漏的相关链接链接上发现了这个资源,它使用Windows资源管理器来监视内存。 - Eduardo Wada
不幸的是,我能想到的最好的方法就是在你的选项卡中放置一些带有大字符串的JavaScript,自动切换选项卡/点击直到满意,然后检查资源管理器。 - Eduardo Wada
刚想起另一个在这种情况下可能有用的提示,即调试器语句在任何浏览器中都像断点一样运作,因此您可以在代码中放置一个断点,然后使用Firebug或Chrome开发者工具在另一个浏览器中进行调试。 - Eduardo Wada

2

您只需要在文档准备就绪事件中收集所有选项卡中的脚本。以下代码查找元素的所有脚本子元素,执行它们,然后销毁它们,以便它们不能再次执行:

function GlobalEvalScriptAndDestroy(element) {
var allScriptText = CollectScriptAndDestroy(element);
jQuery.globalEval(allScriptText);}



function CollectScriptAndDestroy(element) {
var allScriptText = "";
if (element.tagName == "SCRIPT") {
    allScriptText = element.text;
    $(element).remove();
}
else {
    var scripts = $(element).find("script");
    for (var i = 0; i < scripts.length; i++) {
        allScriptText += scripts[i].text;
    }
    scripts.remove();
}
return allScriptText;}

谢谢Francesco。我已经在文档准备好事件中收集了脚本。我也读了Eduardo的回答,他说可能脚本是GC...感谢提供脚本。我已经在每个选项卡显示时执行$('#menutabs .ui-tabs-hide').children().remove();$(".ui-tabs-hide").empty();,至少对于HTML来说它可以工作。你不认为这应该足够了吗?再次感谢! - Larry
抱歉,Francesco。我想尝试你的解决方案。你能告诉我在哪里放置代码以及如何调用它吗?我猜我必须在选项卡的显示事件上调用它。谢谢。 - Larry
不要这样做。在应用选项卡插件的原始div上应用插件之前...也就是在文档准备事件中。这样,当选项卡被创建时,脚本已经被删除了...这样你就可以确保它们的存在不会干扰插件。 - Francesco Abbruzzese

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