Consider this example of pretty standard method in Angular Js, which updates the view:
$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... :-(";
});
};
This is of course hypothetical example, but it nicely shows pattern, which could be described as reusing of local variables from within AJAX callbacks.
Of course in both handlers (success
and error
) we are creating a closure over hugeData
which is directly referenced from callback handlers.
My question is: since the result of AJAX call can be only either success or failure, will reusing of this code cause the memory leak over time? I would answer "yes", but I couldn't reliably prove this one in my local tests.
I'd like some more experienced guru to explain this one for me. I'd love response from anyone working with Angular on daily basis, but any jquery responses are welcome as well.
An example of memory leakThe memory leak would occur if the floor number requested is the same floor that the elevator is on; the condition for releasing the memory would be skipped. Each time this case occurs, more memory is leaked. Cases like this would not usually have any immediate effects.
To find a memory leak, look at how much RAM the system is using. The Resource Monitor in Windows can be used to accomplish this. In Windows 8.1 and Windows 10: To open the Run dialogue, press Windows+R, then type "resmon" and click OK.
DEFINITION A memory leak is the gradual deterioration of system performance that occurs over time as the result of the fragmentation of a computer's RAM due to poorly designed or programmed applications that fail to free up memory segments when they are no longer needed.
You will have a memory leak as soon as you return the result of $http()
call (or whatever object or function that has access to hugeData
) into the outer scope of fetchResults
.
With your code, nothing big is exposed directly outside of fetchResults
, and the result of $http()
call will live until it either succeeds or fail, then calling the corresponding callback, finally getting GC'ed.
See for insights: http://jibbering.com/faq/notes/closures/#clIdRes
As @ŁukaszBachman observes, this does not guarantee that there are no memory leaks. Any dangling reference to your big object or to your callback with big object in scope, will cause woe.
So, let's check into $q
implementation ($http
is based on $q
).
If you check https://github.com/angular/angular.js/blob/master/src/ng/q.js#L191, you can see that the resolve()
method of the deferred first copies the list of registered callbacks in a variable local to the method:
var callbacks = pending;
subsequently nullifies the external pending
(that was defined at the defer
level)
pending = undefined;
then, at next tick, executes the callbacks. Things may get complicated by the fact that the callback's argument may be a deferred itself (adding a further delay to execution), but at most you could get into an infinite loop. (And that's not funny!). If you are lucky enough not to get into the loop, then at some point the callback array is exhausted, and then there is no reference whatsoever to the callback list, so it's available for GC.
But.
Things may go wrong if you force them to.
You may use arguments.callee inside a callback.
You can throw beer on your keyboard too.
If you jump out of the window, unless you live on the first floor, you will probably get hurt.
Happy EcmaScripting!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With