简体   繁体   中英

Is this AJAX pattern a memory leak?

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.

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!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM