Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

fix($rootScope): stop memory leak in IE9 #11786

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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