Memory leak:
A memory leak is caused when allocated memory is not released when it is not needed leading to memory consumption that grows over time. It is a very common issue in angular applications, which if not handled properly can gradually degrade application performance and can result in unexpected application crashes as applications start consuming excessive memory resources.
In Angular, memory leaks often happen when components or other objects are not properly disposed of which as a result prevents the garbage collector from freeing up that allocated memory. There are several common mistakes that lead to memory leakage, for example: not unsubscribing to observables, not removing event listeners or timers after use, etc.
We will be discussing in detail about the memory leaks caused by not unsubscribing an observable, ways to unsubscribe it to prevent it, and best practices related to it.
Before diving deep into it, let’s see what an observable is.
Observables:
As mentioned in Angular official docs an observable is an object that can emit one or more values over time. Observables can emit one or more values synchronously or asynchronously.
An Observable can emit multiple values of any type – messages, events, or literals. And the reason why it is being used to handle HTTP responses is as an event emitter. Observables are declarative, just like a function. This means it won’t start publishing values until a consumer subscribes to it.
This is an example of an observable. number$ is an observable, created using of the operator from RxJs. The number won’t start emitting values until we subscribe to it.
The above statement will print number 1,2,3 synchronously on the console.
Now in our next example, it will print on the console “received value” after every second. Until we
unsubscribe to it.
Getting data/values from an observable is like getting water from a tap, we won’t get water from the tap until we open it (i.e. subscribe to the observable) and once we are done with it, we should close the tap (i.e. unsubscribe from it).
Just like the tap, we need to close the tap, it is always a good practice to unsubscribe from Observables. Below are mentioned three different ways to unsubscribe the observables.
- Store each subscription in a separate variable and on ngOnDestroy (i.e. on component destruction) unsubscribe each subscription.
This looks kind of simpler but doing this in big complex real-time applications would be difficult, as there would be too many subscriptions to delete, which will result in so many class variables to take care of. We can also push these subscriptions to an array of subscriptions and unsubscribe from them in ngOnDestroy.
2. We can use the power of RxJs to unsubscribe using operators like takeUntil and subject.
Now in the below example, we have added an operator takeUntil from RxJs, which takes one notifier, as soon it emits from method next(), the subscription to source$ will be cancelled.
- Now my personal best is to use async pipe and make your component as reactive as possible. If there is some data that is going to change with time, for example depending on HTTP response or some event. Also, when a new value is emitted, the async pipe marks the component to be checked for changes. It runs change detection to update the UI accordingly.
Async pipe subscribes to the observable as soon as the component is initialized and
unsubscribes as soon as the component gets destroyed to avoid potential memory leaks.
Here data$ is an observable used with async pipe to read through the observable stream.
To summarize, in Angular when we subscribe to an observable stream, we should unsubscribe from it as well to prevent potential memory leaks. We saw three ways to unsubscribe from an observable, we can use any of the above as per our convenience.