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

Commit 8fe781f

Browse files
fix($rootScope): stop IE9 memory leak when destroying scopes
Ensure that all child scopes are completely disconnected when a parent is destroyed. Closes #10706 Closes #11786
1 parent 23932a2 commit 8fe781f

File tree

2 files changed

+54
-10
lines changed

2 files changed

+54
-10
lines changed

src/ng/rootScope.js

+26-10
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,29 @@ function $RootScopeProvider() {
101101
$event.currentScope.$$destroyed = true;
102102
}
103103

104+
function cleanUpScope($scope) {
105+
106+
if (msie === 9) {
107+
// There is a memory leak in IE9 if all child scopes are not disconnected
108+
// completely when a scope is destroyed. So this code will recurse up through
109+
// all this scopes children
110+
//
111+
// See issue https://github.com/angular/angular.js/issues/10706
112+
$scope.$$childHead && cleanUpScope($scope.$$childHead);
113+
$scope.$$nextSibling && cleanUpScope($scope.$$nextSibling);
114+
}
115+
116+
// The code below works around IE9 and V8's memory leaks
117+
//
118+
// See:
119+
// - https://code.google.com/p/v8/issues/detail?id=2073#c26
120+
// - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
121+
// - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
122+
123+
$scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
124+
$scope.$$childTail = $scope.$root = $scope.$$watchers = null;
125+
}
126+
104127
/**
105128
* @ngdoc type
106129
* @name $rootScope.Scope
@@ -897,16 +920,9 @@ function $RootScopeProvider() {
897920
this.$on = this.$watch = this.$watchGroup = function() { return noop; };
898921
this.$$listeners = {};
899922

900-
// All of the code below is bogus code that works around V8's memory leak via optimized code
901-
// and inline caches.
902-
//
903-
// see:
904-
// - https://code.google.com/p/v8/issues/detail?id=2073#c26
905-
// - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
906-
// - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
907-
908-
this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
909-
this.$$childTail = this.$root = this.$$watchers = null;
923+
// Disconnect the next sibling to prevent `cleanUpScope` destroying those too
924+
this.$$nextSibling = null;
925+
cleanUpScope(this);
910926
},
911927

912928
/**

test/ng/rootScopeSpec.js

+28
Original file line numberDiff line numberDiff line change
@@ -1211,6 +1211,34 @@ describe('Scope', function() {
12111211
expect(child.parentModel).toBe('parent');
12121212
expect(child.childModel).toBe('child');
12131213
}));
1214+
1215+
1216+
if (msie === 9) {
1217+
// See issue https://github.com/angular/angular.js/issues/10706
1218+
it('should completely disconnect all child scopes on IE9', inject(function($rootScope) {
1219+
var parent = $rootScope.$new(),
1220+
child1 = parent.$new(),
1221+
child2 = parent.$new(),
1222+
grandChild = child1.$new();
1223+
parent.$destroy();
1224+
1225+
$rootScope.$digest();
1226+
1227+
expect(isDisconnected(parent)).toBe(true);
1228+
expect(isDisconnected(child1)).toBe(true);
1229+
expect(isDisconnected(child2)).toBe(true);
1230+
expect(isDisconnected(grandChild)).toBe(true);
1231+
1232+
function isDisconnected($scope) {
1233+
return $scope.$$nextSibling === null &&
1234+
$scope.$$prevSibling === null &&
1235+
$scope.$$childHead === null &&
1236+
$scope.$$childTail === null &&
1237+
$scope.$root === null &&
1238+
$scope.$$watchers === null;
1239+
}
1240+
}));
1241+
}
12141242
});
12151243

12161244

0 commit comments

Comments
 (0)