![]()
Memory leaks in AngularJS can degrade performance, leading to sluggish applications and excessive memory consumption. They typically occur due to unmanaged event listeners, lingering scopes, and unremoved watchers. Proper memory management ensures smooth performance and prevents unnecessary resource consumption.
1. Common Causes of Memory Leaks
| Cause | Description |
|---|---|
| Unremoved Event Listeners | Listeners attached to elements or $scope remain even after scope destruction. |
Too Many Watchers ($watch) | Excessive use of $watch() keeps references active unnecessarily. |
| Dangling DOM Elements | Elements persist in memory if not properly removed from the DOM. |
Uncleared Intervals ($interval, $timeout) | Timers continue running even after controllers or components are destroyed. |
| $rootScope Accumulation | Data stored in $rootScope remains persistent throughout the app lifecycle. |
2. Best Practices to Prevent Memory Leaks
2.1. Remove Event Listeners
AngularJS doesn’t automatically remove event listeners. Always clean up manually.
Example: Removing DOM Event Listeners
app.controller("MyController", function($scope, $element) {
function onClickHandler() {
console.log("Element clicked!");
}
$element.on("click", onClickHandler);
$scope.$on("$destroy", function() {
$element.off("click", onClickHandler); // Remove event listener
});
});
Prevents event listeners from staying in memory after the controller is destroyed.
2.2. Unsubscribe from $watch
Too many $watch calls can slow down performance. Remove unused watchers when they’re no longer needed.
Example: Unsubscribing from $watch()
app.controller("MyController", function($scope) {
var unwatch = $scope.$watch("someData", function(newValue, oldValue) {
console.log("Data changed:", newValue);
});
$scope.$on("$destroy", function() {
unwatch(); // Unsubscribe from watcher
});
});
Prevents watchers from consuming memory when the scope is destroyed.
2.3. Stop $interval and $timeout on Destroy
AngularJS timers keep running even after scope destruction. Always cancel them manually.
Example: Clearing $interval
app.controller("MyController", function($scope, $interval) {
var intervalPromise = $interval(function() {
console.log("Interval running...");
}, 1000);
$scope.$on("$destroy", function() {
$interval.cancel(intervalPromise);
});
});
Prevents memory leaks by ensuring that intervals stop when the scope is destroyed.
2.4. Remove Global References in $rootScope
Since $rootScope persists across the application lifecycle, avoid storing temporary data inside it.
Example: Removing Data from $rootScope
app.run(function($rootScope) {
$rootScope.globalData = "Some persistent data";
$rootScope.$on("$destroy", function() {
delete $rootScope.globalData; // Remove unnecessary global data
});
});
Prevents global memory accumulation.
2.5. Use ng-if Instead of ng-show/ng-hide
Using ng-show and ng-hide keeps elements in the DOM, whereas ng-if removes them completely.
Example: Using ng-if for Better Performance
<!-- ng-show keeps the element in memory -->
<div ng-show="isVisible">This stays in the DOM</div>
<!-- ng-if removes the element completely -->
<div ng-if="isVisible">This gets removed when false</div>
Reduces DOM memory usage.
2.6. Destroy $scope Properly
If using custom directives, always clean up $scope.
Example: Cleaning Up Scope in Directives
app.directive("customDirective", function() {
return {
restrict: "E",
scope: {},
link: function(scope, element) {
scope.$on("$destroy", function() {
element.remove(); // Remove from DOM
scope = null; // Free up memory
});
}
};
});
Ensures that isolated scope directives don’t leak memory.
2.7. Use One-time Binding for Static Data
One-time binding (::) reduces the number of watchers, improving performance.
Example: Using One-Time Binding
<p>{{ ::user.name }}</p> <!-- One-time binding, no watcher needed -->
Reduces memory consumption by avoiding unnecessary watchers.
3. Debugging Memory Leaks
3.1. Using Chrome DevTools
- Open Chrome DevTools (
F12orCtrl + Shift + I). - Go to Memory tab.
- Select Heap Snapshot and take a snapshot before and after running your AngularJS app.
- Look for objects that persist unexpectedly.
3.2. Checking Watchers Count
Use this snippet in the browser console to count AngularJS watchers:
angular.element(document).injector().invoke(function($rootScope) {
var watchers = 0;
var countWatchers = function(scope) {
if (scope.$$watchers) {
watchers += scope.$$watchers.length;
}
angular.forEach(scope.$$childHead, countWatchers);
};
countWatchers($rootScope);
console.log("Total watchers:", watchers);
});
Helps identify excessive watchers.
