fix($rootScope): fix potential memory leak when removing scope listeners

When removing listeners they are removed from the array but the array size
is not changed until the event is fired again. If the event is never fired
but listeners are added/removed then the array will continue growing.

By changing the listener removal to `delete` the array entry instead of setting
it to `null` browsers can potentially deallocate the memory for the entry.

Fixes #16135
Closes #16161
This commit is contained in:
Jason Bedard
2017-08-08 23:24:17 -07:00
committed by Martin Staffa
parent e5fb92978f
commit 97d0224ae6
2 changed files with 19 additions and 1 deletions
+4 -1
View File
@@ -1273,7 +1273,10 @@ function $RootScopeProvider() {
return function() {
var indexOfListener = namedListeners.indexOf(listener);
if (indexOfListener !== -1) {
namedListeners[indexOfListener] = null;
// Use delete in the hope of the browser deallocating the memory for the array entry,
// while not shifting the array indexes of other listeners.
// See issue https://github.com/angular/angular.js/issues/16135
delete namedListeners[indexOfListener];
decrementListenerCount(self, 1, name);
}
};
+15
View File
@@ -2438,6 +2438,21 @@ describe('Scope', function() {
}));
// See issue https://github.com/angular/angular.js/issues/16135
it('should deallocate the listener array entry', inject(function($rootScope) {
var remove1 = $rootScope.$on('abc', noop);
$rootScope.$on('abc', noop);
expect($rootScope.$$listeners['abc'].length).toBe(2);
expect(0 in $rootScope.$$listeners['abc']).toBe(true);
remove1();
expect($rootScope.$$listeners['abc'].length).toBe(2);
expect(0 in $rootScope.$$listeners['abc']).toBe(false);
}));
it('should call next listener after removing the current listener via its own handler', inject(function($rootScope) {
var listener1 = jasmine.createSpy('listener1').and.callFake(function() { remove1(); });
var remove1 = $rootScope.$on('abc', listener1);