AngularJS Memory Leak Playground (Simple example)

Playground for detecting potential memory leaks when using AngularJS. Counting controller initializations and displaying memory usage.

HTML

<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular-route.js"></script>
<script type="text/ng-template" id="tab.html">
    <p>Auto switching: 
        <button ng-click="startAutoSwitching()">start</button>
        <button ng-click="stopAutoSwitching()">stop</button>
    </p>
    <p>Name: {{name}}</p>
    <p>Controller instantiations: {{controllerInstantiations}}</p>
    <p>Heap size: {{heapSize}}</p>
</script>
<div class="main">
    <ul class="menu">
        <li><a href="#/this">This</a></li>
        <li><a href="#/that">That</a></li>
    </ul>
    <ng-view></ng-view>
</div>

CSS

.main {
    font-family: Helvetica, Arial, sans-serif;
    font-size: 14px;
    padding: 10px;
}
.menu {
    margin-bottom: 10px;
}
.menu li {
    display: inline;
}
p {
    margin-bottom: 10px;
}

JavaScript

var app = angular.module('myApp', ['ngRoute']);
app.value('constants', {
    'numOfRandomNumbers': 1000000,
    'shouldRegister': true,
    'shouldUnregister': true,
    'autoSwitchTimeout': 1000 });

function heapSize() {
    if (!performance.memory) {
        return('!!! performance.memory not supported !!!');
    }
    if (!performance.memory.usedJSHeapSize) {
        return('!!! performance.memory.usedJSHeapSize not supported !!!');
    }
    return Math.round(performance.memory.usedJSHeapSize / 10000, 2) / 100 + ' MB';
}

function createArray(numOfRandomNumbers) {
    var result = [];
    for(var i=0; i<numOfRandomNumbers; i++) {
        result.push(Math.random());
    }
    return result;
}
    
app.controller('Ctrl', function ($rootScope, $scope, constants) {
    $scope.name = ($rootScope.controllerInstantiations % 2 == 0) ? 'This' : 'That';
    $scope.controllerInstantiations = $rootScope.controllerInstantiations++;
    $scope.heapSize = heapSize();
    $scope.randomArray = createArray(constants.numOfRandomNumbers);
    if (constants.shouldRegister) {
        var unregister = $rootScope.$on('someEvent', function() {
            $scope.eventOccurred = true;
        });
        if (constants.shouldUnregister) {
            $scope.$on('$destroy', function() {
                unregister();
            });
        }
    }
});

app.config(function ($routeProvider) {
    $routeProvider.when('/this', {
        templateUrl: 'tab.html',
        controller: 'Ctrl'
    })
        .when('/that', {
        templateUrl: 'tab.html',
        controller: 'Ctrl'
    })
        .otherwise({
        redirectTo: '/this'
    });
});
app.run(['$rootScope',
    '$timeout', '$location', 'constants', function ($rootScope, $timeout, $location, constants) {
    $rootScope.controllerInstantiations = 0;
    $rootScope.autoSwitching = false;

    function scheduleTabSwitch(autoSwitchTimeout) {
        $rootScope.switchPromise = $timeout(function () {
            var path =...