Skip to content

Commit

Permalink
fix($rootScope): stop IE9 memory leak when destroying scopes
Browse files Browse the repository at this point in the history
Ensure that all child scopes are completely disconnected when a parent is
destroyed.

Closes angular#10706
Closes angular#11786
  • Loading branch information
petebacondarwin committed Oct 28, 2015
1 parent 964a901 commit edee9e8
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 10 deletions.
36 changes: 26 additions & 10 deletions src/ng/rootScope.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,29 @@ function $RootScopeProvider() {
$event.currentScope.$$destroyed = true;
}

function cleanUpScope($scope) {

if (msie === 9) {
// There is a memory leak in IE9 if all child scopes are not disconnected
// completely when a scope is destroyed. So this code will recurse up through
// all this scopes children
//
// See issue https://github.com/angular/angular.js/issues/10706
$scope.$$childHead && cleanUpScope($scope.$$childHead);
$scope.$$nextSibling && cleanUpScope($scope.$$nextSibling);
}

// The code below works around IE9 and V8's memory leaks
//
// See:
// - https://code.google.com/p/v8/issues/detail?id=2073#c26
// - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
// - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451

$scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
$scope.$$childTail = $scope.$root = $scope.$$watchers = null;
}

/**
* @ngdoc type
* @name $rootScope.Scope
Expand Down Expand Up @@ -897,16 +920,9 @@ function $RootScopeProvider() {
this.$on = this.$watch = this.$watchGroup = function() { return noop; };
this.$$listeners = {};

// All of the code below is bogus code that works around V8's memory leak via optimized code
// and inline caches.
//
// see:
// - https://code.google.com/p/v8/issues/detail?id=2073#c26
// - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
// - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451

this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
this.$$childTail = this.$root = this.$$watchers = null;
// Disconnect the next sibling to prevent `cleanUpScope` destroying those too
this.$$nextSibling = null;
cleanUpScope(this);
},

/**
Expand Down
28 changes: 28 additions & 0 deletions test/ng/rootScopeSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1211,6 +1211,34 @@ describe('Scope', function() {
expect(child.parentModel).toBe('parent');
expect(child.childModel).toBe('child');
}));


if (msie === 9) {
// See issue https://github.com/angular/angular.js/issues/10706
it('should completely disconnect all child scopes on IE9', inject(function($rootScope) {
var parent = $rootScope.$new(),
child1 = parent.$new(),
child2 = parent.$new(),
grandChild = child1.$new();
parent.$destroy();

$rootScope.$digest();

expect(isDisconnected(parent)).toBe(true);
expect(isDisconnected(child1)).toBe(true);
expect(isDisconnected(child2)).toBe(true);
expect(isDisconnected(grandChild)).toBe(true);

function isDisconnected($scope) {
return $scope.$$nextSibling === null &&
$scope.$$prevSibling === null &&
$scope.$$childHead === null &&
$scope.$$childTail === null &&
$scope.$root === null &&
$scope.$$watchers === null;
}
}));
}
});


Expand Down

0 comments on commit edee9e8

Please sign in to comment.