Skip to content

Commit 80eed91

Browse files
crisbetojosephperrott
authored andcommitted
perf: memory leak when subscribing to zone events (angular#6918)
Fixes a few memory leaks that were caused by subscribing to `NgZone.onMicrotaskEmpty` or `NgZone.onStable`. Because the two streams are `EventEmitters`, the subscription doesn't get GC-ed correctly. These changes switch to converting the emitters to observables before subscribing to them. Fixes angular#6905.
1 parent bf91a89 commit 80eed91

File tree

7 files changed

+19
-16
lines changed

7 files changed

+19
-16
lines changed

src/cdk/a11y/focus-trap.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ export class FocusTrap {
265265
if (this._ngZone.isStable) {
266266
fn();
267267
} else {
268-
first.call(this._ngZone.onStable).subscribe(fn);
268+
first.call(this._ngZone.onStable.asObservable()).subscribe(fn);
269269
}
270270
}
271271
}

src/lib/autocomplete/autocomplete-trigger.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
372372
* stream every time the option list changes.
373373
*/
374374
private _subscribeToClosingActions(): Subscription {
375-
const firstStable = first.call(this._zone.onStable);
375+
const firstStable = first.call(this._zone.onStable.asObservable());
376376
const optionChanges = map.call(this.autocomplete.options.changes, () =>
377377
this._positionStrategy.recalculateLastPosition());
378378

src/lib/datepicker/calendar.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,9 +218,11 @@ export class MdCalendar<D> implements AfterContentInit, OnDestroy {
218218

219219
/** Focuses the active cell after the microtask queue is empty. */
220220
_focusActiveCell() {
221-
this._ngZone.runOutsideAngular(() => first.call(this._ngZone.onStable).subscribe(() => {
222-
this._elementRef.nativeElement.querySelector('.mat-calendar-body-active').focus();
223-
}));
221+
this._ngZone.runOutsideAngular(() => {
222+
first.call(this._ngZone.onStable.asObservable()).subscribe(() => {
223+
this._elementRef.nativeElement.querySelector('.mat-calendar-body-active').focus();
224+
});
225+
});
224226
}
225227

226228
/** Whether the two dates represent the same view in the current view mode (month or year). */

src/lib/datepicker/datepicker.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,9 @@ export class MdDatepicker<D> implements OnDestroy {
323323
componentRef.instance.datepicker = this;
324324

325325
// Update the position once the calendar has rendered.
326-
first.call(this._ngZone.onStable).subscribe(() => this._popupRef.updatePosition());
326+
first.call(this._ngZone.onStable.asObservable()).subscribe(() => {
327+
this._popupRef.updatePosition();
328+
});
327329
}
328330

329331
this._popupRef.backdropClick().subscribe(() => this.close());

src/lib/sidenav/drawer.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,8 +409,11 @@ export class MdDrawerContainer implements AfterContentInit, OnDestroy {
409409
}
410410
// NOTE: We need to wait for the microtask queue to be empty before validating,
411411
// since both drawers may be swapping positions at the same time.
412-
takeUntil.call(drawer.onPositionChanged, this._drawers.changes).subscribe(() =>
413-
first.call(this._ngZone.onMicrotaskEmpty).subscribe(() => this._validateDrawers()));
412+
takeUntil.call(drawer.onPositionChanged, this._drawers.changes).subscribe(() => {
413+
first.call(this._ngZone.onMicrotaskEmpty.asObservable()).subscribe(() => {
414+
this._validateDrawers();
415+
});
416+
});
414417
}
415418

416419
/** Toggles the 'mat-drawer-opened' class on the main 'md-drawer-container' element. */

src/lib/snack-bar/snack-bar-container.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -186,13 +186,9 @@ export class MdSnackBarContainer extends BasePortalHost implements OnDestroy {
186186
* errors where we end up removing an element which is in the middle of an animation.
187187
*/
188188
private _completeExit() {
189-
// Note: we shouldn't use `this` inside the zone callback,
190-
// because it can cause a memory leak.
191-
const onExit = this._onExit;
192-
193-
first.call(this._ngZone.onMicrotaskEmpty).subscribe(() => {
194-
onExit.next();
195-
onExit.complete();
189+
first.call(this._ngZone.onMicrotaskEmpty.asObservable()).subscribe(() => {
190+
this._onExit.next();
191+
this._onExit.complete();
196192
});
197193
}
198194
}

src/lib/tooltip/tooltip.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ export class MdTooltip implements OnDestroy {
392392
this._tooltipInstance.message = this.message;
393393
this._tooltipInstance._markForCheck();
394394

395-
first.call(this._ngZone.onMicrotaskEmpty).subscribe(() => {
395+
first.call(this._ngZone.onMicrotaskEmpty.asObservable()).subscribe(() => {
396396
if (this._tooltipInstance) {
397397
this._overlayRef!.updatePosition();
398398
}

0 commit comments

Comments
 (0)