1

It seems I have memory leak in our application and its time to bang my head against wall trying to find it hard but I think I am noob to do that since its my first time and being unfamiliar with chrome dev tool.

Overview of application architecture:

We have a single page application developed on angularjs framework. we are consuming API's to get data and do CRUD operations. each module have multiple pages (say we have banks, branches, employees, managers, accountants etc) and each module have multiple states (we are using angular UI-router). I have created a angular factory Object on my top module which needs to be stay alive in whole life cycle of application and each module will register their sub model on it in which I have my page settings API's and list of fields which map to object properties return by API.

something like below:

angular
    .module('mainModule')
    .factory('model', apiService);

/** @ngInject */
function apiService()
{
    var model= {};

    // some code
    return model;
}

and for each sub module I am registering my subModel in run block as below:

(function() {
'use strict';

angular
    .module('subModule')
    .run(runBlock);

/* @ngInject */
function runBlock(model) {

    if (angular.isUndefined(model.subModel)) {
        model.subModel= {};
    }
     model.subModel.banks= {
        modelPath: 'subModel.banks',
        fields: [
            { field: 'Id', type: 'int', displayLabel: 'BANKS.FIELDS.ID', visible: false, creatable: false, editable: false, inputType: 'number',
                validation: {isRequired: true, maxLen: -1, minLen: 1, pattern: '', messages: {isRequired: 'VALIDATIONS.REQUIRED', maxLen: 'VALIDATIONS.MAXLEN', minLen: '', pattern: ''}} },
             //more similer fields which translate API's object property to what I want to show in UI
            ],
        title: 'CATALOGS.NAV',
        icon: 'icon-book-open',
        idField: 'Id',
        lookupDisplayField: 'BankName',
        methods: {
            getList         :   getBanks
        },
        views: {
            mainList: {
                columns         : [ 'Id', //and more columns],
                //more settings
            },
            mainDetails: {
             //settings for detail page
            },
        }
    };

    function getBanks() {
           return promise
        }
    .......... and so on

(if there is a name for this architecture I would like to know)

I have a directive which will create my UI page, this directive will get data returned by API, instance of subModel which is created on runBlock of sub Module and they are 2way binded to my directive

[my controller look somtehing like this]

(function() {
'use strict';

angular
    .module('subModule')
    .controller('banksController', banksController);

/* @ngInject */
function banksController(model) {
    var vm = this;

    //Data
    vm.banksModel = model.subModel.banks;

    vm.banks= [];
    vm.isLoading = false;


    //Methods
    vm.refreshItems = refreshItems;

    activate();

    ////////////////

    function activate() {
        refreshItems();
    }

    function refreshItems() {
        vm.isLoading = true;

        vm.banksModel.methods.getList()
        .then(function (response) {
            vm.banks= response.data;
        })
        .finally(function () {
            vm.isLoading = false;
        });
    }
    .......... and so on

[template]

<my-directive my-data="vm.banks" my-model="vm.banksModel"
            my-is-loading="vm.isLoading"...></my-directive>
 ....

[directive]

(function() {
'use strict';

angular
    .module('highLvlModule')
    .directive('myDirective', myDirectiveDirective);


function myDirectiveDirective() {
    var directive = {
        bindToController: true,
        controller: myDirectiveController,
        controllerAs: 'vm',
        link: link,
        restrict: 'E',
        scope: {
            myData: '=',
            myModel: '=',
            isLoading: '=?txIsLoading',
            ..//otherbindings
        },
        templateUrl: 'app/..../.../my-directive.html'
    };
    return directive;

    function link(scope, element) {
        scope.element = element;

        if (angular.isUndefined(scope.vm.isLoading)) {
            scope.vm.isLoading = false;
        }

    }
}
/* @ngInject */
function myDirectiveController(model, $window, $scope, $rootScope, $document, $timeout, $localStorage, dataFactory,$filter, hotkeys, settingsFactory, dragulaService) {


    var vm = this;

    //data

    vm.fields = vm.myModel.fields;
    ...// and so on

myDirective will render the page based on the myModel and populate data based on myData .

somewhere something is wrong in my application and I have memory leak. [Question]:does vm get destroyed when controller or directive controller get destroyed? if look at refreshItems method in banksController we are using vm.banks in context of a closure, is the the reason of leak?

Why I think we have a leak If I navigate from one page to another and go page to the previous page (after click on garbage collector) the memory usage do not reach to where it was and on repeating this action the memory keep extending by 30-34 MB which is the size of data in one of the module (I am keep switching between two module one having very light data and the other having very big data 80K records) following is Heap snapshot and did the following steps to create that

  1. refresh the browser on thin module wait for it to be loaded completely
  2. switch to fat module wait for it to be loaded completely
  3. switch back to thin module and took heap snapshot
  4. switch back to fat module and took heap snapshot
  5. switch back again to thin module and took heap snapshot

and this is the result(objects created between 3 to 4 and still alive in steps 5 will be displayed in summery)

enter image description here

enter image description here

enter image description here

if we see in above image Array @2145987 and Array @2145989 are the record created in steps 2 and for and still alive in step 5

here is the timeline snapshot enter image description here

  1. refresh on thin module and wait for 5 secs and start the timeline
  2. after 12 seconds click on GC
  3. after 8 sec (20000ms) navigate to fat module
  4. fat module loaded at 110000ms
  5. after 10 sec (120000ms) navigate to thin module and it loaded imidiatly
  6. after 10 sec (130000ms) navigate back to fat module and it loaded at 133000ms
  7. after 10 sec navigate back to thin module and it loaded immediately (143000ms)
  8. after 10 sec click on GC at 153000ms

timeline snapshot below where taken after performing above steps (refresh on thin, navigate to fat, navigate to thin, navigate to fat then thin and then start timeline)

enter image description here above image is timeline snapshot of switching between those two modules without executing GC between each navigation

enter image description here above image is timeline snapshot of switching between those two modules with executing GC between each navigation

Sohail Faruqui
  • 442
  • 11
  • 27
  • I noticed, in places where I make HTTP get request, even after navigating to another independent state (where the controller in which we make HTTP request destroyed) control goes inside the promise and initialize the data, could that be the leak? how to invalidate that promise – Sohail Faruqui Feb 06 '17 at 07:17

1 Answers1

1

Try to use scope.on('$destroy', () => { /* delete your model here */ }

You can read more about angular js $destroy: AngularJS - Does $destroy remove event listeners?

Community
  • 1
  • 1
Aurimas V.
  • 11
  • 2