2013-06-04 22 views
6

考虑角JS,其中更新视图相当标准的方法的该实施例中:这个AJAX模式是内存泄漏吗?

$scope.fetchResults = function() { 
    // Some local variable that will cause creation of closure 
    var hugeData = serviceX.getMilionRecords(); 

    // Any call to any resource with success and error handlers. 
    $http({ 
     method: "GET", 
     url: "/rest-api/bulk-operation-x", 
     params: { someParam: hugeData.length } 

    }).success(function() { 
     var length = hugeData.length; 
     $scope.reportToUser("Success, that was " + length + " records being processed!"; 

    }).error(function() { 
     var length = hugeData.length; 
     $scope.reportToUser("Something went wrong while processing " + length + " records... :-("; 
    }); 
}; 

这是当然假设的例子,但它很好地示出了图案,这可能从内被描述为复用的局部变量的AJAX回调。在这两个处理器(successerror)我们正在创造过hugeData封闭这是直接从回调处理程序中引用

当然。

我的问题是:由于AJAX调用的结果只能是成功或失败,这段代码是否会重复使用导致内存泄漏?我会回答“是”,但我无法在我的本地测试中可靠地证明这一点。

我想要一些更有经验的导师为我解释这一个。我很喜欢每天使用Angular的任何人的回复,但是也欢迎任何jQuery响应。

+2

“内存泄漏”是一个非常具体的术语,指的是分配的内存,随后永远不会释放。它仅适用于手动完成内存管理的上下文。鉴于JS管理对程序员来说是透明的,只有在某些情况下讨论导致错误实现泄漏内存的编码模式时,内存泄漏才是相关的,就像旧版本的IE一样。我不确定这个问题是否有意义。 – Jon

+0

我不同意@ Jon的评论。一个不好的做法可能会导致内存泄漏,比如填充全局范围内的对象等。在这种情况下,它不会,因为一旦回调完成(并且$ http实例被清除,hugeData变量就会被删除up) – rewritten

+0

@Jon,通过透明内存管理,您可以非常容易地创建任何语言的内存泄漏。爪哇,斯卡拉,JavaScript,C#,你的名字。 @rewritten,这正是我所说的。你怎么知道当''success()'完成时'hugeData'会被删除?从语言的角度来看,'error()'中使用的闭包可能会在稍后的某个时间执行。除非Angular在底层做了一些事情(比如在完成时取消其他回调),那么我们可能会在这里发生内存泄漏。 –

回答

4

为您返回$http()通话(或任何对象或函数可以访问hugeData)进入fetchResults外范围的结果,您便会有内存泄漏。

用你的代码,没有什么大的直接暴露在fetchResults之外,并且调用$http()的结果直到它成功或失败,然后调用相应的回调,最终得到GC'ed。

见的见解:http://jibbering.com/faq/notes/closures/#clIdRes

正如@ŁukaszBachman指出,这并不能保证不存在内存泄漏。任何对你的大对象或对大范围对象的回调的引用都会造成不良后果。

因此,让我们来看看$q的实现($http基于$q)。

如果检查https://github.com/angular/angular.js/blob/master/src/ng/q.js#L191,你可以看到,递延第一副本的resolve()方法注册的回调的一个变量列表本地的方法:

var callbacks = pending; 

随后勾销外部pending(即定义在defer级别)

pending = undefined; 

然后,在下一个时间点执行回调。事情可能会变得复杂,因为回调的参数本身可能会被延迟(进一步延迟执行),但最多可能会陷入无限循环。 (这并不好笑!)。如果你足够幸运不会进入循环,那么在某些时候回调数组已经耗尽,然后没有任何参考回调列表,所以它可用于GC。

但是。

如果你强迫他们的事情可能会出错。

您可以在回调中使用arguments.callee。

你也可以在你的键盘上扔啤酒。

如果你跳出窗外,除非你住在一楼,否则你可能会受伤。

Happy EcmaScripting!

+0

为什么你确定'$ http()调用的结果将存在,直到它成功或失败? AFAIU封闭管理我的JS引擎本身,所以从我的例子你不能确定,对吧?你将不得不检查'$ http'的内部impl来验证第二个回调得到处理,因此可以被GC释放。 我正确吗? (我已经将提供的文章添加到我的阅读列表中) –

+0

检查'$ q'实现,特别是'resolve'方法(https://github.com/angular/angular.js/blob/master/src/ng/ q.js#L191)。一旦promise被解析,'pending'回调链就完全从promise的范围中移除,并且它被解析方法中的另一个变量内化。解决方案完成后,将不再提及回调,使其可用于GC。 – rewritten

+0

换句话说,你是对的。并且完全按照这种方式完成,在解析过程中会清除注册回调的完整列表。 – rewritten