[V3] Memory leak


#1

I am developing an App with Cordova and Framework7 - Core (v3.6.7) and I have the following problem:

As I navigate through the different screens, memory usage increases more and more until the operating system kills the App.

I have tried all possible combinations of stackPages, keepAvailable, history configuration. I found that other users had the same problem (posted in the forum, github, stackoverflow) but it is not clear if they have been able to solve it. In my case, none of the possible solutions gave me results.

Can someone tell me the correct configuration so that the screens are removed from the DOM once they leave the view? I nedd keep only page-home in the stack. Is it possible to achieve it or do I have to migrate to Vue-Webpack?

Below are general details of my App:
index.html:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="Content-Security-Policy" content="default-src * 'self' 'unsafe-inline' 'unsafe-eval' data: gap: content:">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui, viewport-fit=cover">
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="default">
  <meta name="theme-color" content="#2196f3">
  <meta name="format-detection" content="telephone=no">
  <meta name="msapplication-tap-highlight" content="no">
  
  <title>Test FWK7 V3</title>

  <link rel="stylesheet" href="css/lib/framework7/framework7.min.css">
  <link rel="stylesheet" href="css/lib/framework7/icons.css">
	    
  <link rel="stylesheet" href="css/common.css">
</head>
<body>
  <div id="app">
    <div class="statusbar"></div>
    <!-- Left panel with cover effect-->
    <div class="panel panel-left panel-cover theme-dark">
      <div class="view panel-view">
        <div class="page">
        	<div id="div-img-main-menu-container">
        		<img src="img/main-menu-with-label.jpg" class="img-main-menu">
        	</div>
			<div class="page-content menu-lateral">
            <div id="div-menu-container" class="list">
              <ul>
                <li class="li-mnu-item">
                  <a onclick="openPageHomeRemoveHistory()" class="item-content panel-close">
                    <div class="item-media">
                      <i class="material-icons">home</i>
                    </div>
                    <div class="item-inner div-label-mnu">
                      <div class="item-title">page-home</div>
                    </div>
                  </a>
                </li>
                <li class="li-mnu-item">
                  <a onclick="openPage('page-01')"  class="item-content panel-close">
                    <div class="item-media">
                      <i class="material-icons">phone_android</i>
                    </div>
                    <div class="item-inner div-label-mnu">
                      <div class="item-title">page-page-01</div>
                    </div>
                  </a>
                </li>
                <li class="li-mnu-item">
                  <a onclick="openPage('page-02')"  class="item-content panel-close">
                    <div class="item-media">
                      <i class="material-icons">list</i>
                    </div>
                    <div class="item-inner div-label-mnu">
                      <div class="item-title">page-02</div>
                    </div>
                  </a>
                </li>
                <li class="li-mnu-item">
                  <a onclick="openPage('page-03')"  class="item-content panel-close">
                    <div class="item-media">
                      <i class="material-icons">list</i>
                    </div>
                    <div class="item-inner div-label-mnu">
                      <div class="item-title">page-03</div>
                    </div>
                  </a>
                </li>
                <li class="li-mnu-item">
                  <a onclick="openPage('page-04')"  class="item-content panel-close">
                    <div class="item-media">
                      <i class="material-icons">account_circle</i>
                    </div>
                    <div class="item-inner div-label-mnu">
                      <div class="item-title">page-04</div>
                    </div>
                  </a>
                </li>
              </ul>
            </div>
          </div>
        </div>
      </div>
    </div>
      
    <!-- main view -->
    <div id="view-home" url="/" class="view view-main">
    </div>

  </div>
  <script src="cordova.js"></script>
  <script src="js/lib/framework7/framework7.min.js"></script>
  <script src="js/routes.js"></script>
  <script src="js/common.js"></script>

</body>
</html>

common.js:

// Dom7
var $$ = Dom7;

// Framework7 App main instance
var myApp = new Framework7({
    root: '#app', // App root element
    id: 'com.rma.testfwk7v3', // App bundle ID
    name: 'TEST FWK7 V3', // App name
    //Inicializo la aplicación de forma manual en el jQuery Ready para que se pueda disparar los eventos de página de
    // la página Inicial
    init: false,
    theme: 'md',
    panel: {
        swipe: 'left'
    },
    navbar: {
        scrollTopOnTitleClick: true,
    },
    actions: {
        convertToPopover: false,
    },
    statusbar: {
        enabled: false
    },
    input: {
        scrollIntoViewOnFocus: true,
        scrollIntoViewCentered: true,
    },
    // App routes
    routes: routes
});

// Init/Create views
var mainView = myApp.views.create('#view-home', {
    url: '/',
    stackPages: false,
    main: true
});



//#################################################################################################################
//########################################           CORDOVA           ############################################
//#################################################################################################################


// Attach an event listener - This is an event that fires when PhoneGap is fully loaded
document.addEventListener("deviceready", onDeviceReady, false);

function onDeviceReady() {

    myApp.init();
	//Attach an event listener - This is an event that fires when the user presses the device's back button
    document.addEventListener("backbutton", onBackKeyDown, false);

}


function onBackKeyDown() 
{
    var currentPage = mainView.router.currentRoute.name;
	if ( currentPage != 'page-home' )
    {
    	mainView.router.back('/', 
        {
            force: true
        });
    }
    else 
    {
        navigator.app.exitApp();
    }
}

//#################################################################################################################
//########################################           CUSTOM FUNCTIONS           ############################################
//#################################################################################################################

function openPage(pageName) 
{
    
    // turn off stackPages
    //mainView.router.params.stackPages=false;
    // navigate with no history
    mainView.router.navigate('/' + pageName + '/');
    // turn on stackPages
    //mainView.router.params.stackPages=true;
}

function openPageHomeRemoveHistory()
{
    //myApp.router.navigate('/' + pageName + '/');
    mainView.router.navigate('/',{clearPreviousHistory: true});
}

function showDialogLoading() 
{
	myApp.dialog.progress(lblConnecting);
}

function showCustomDialogLoading(message) 
{
	myApp.dialog.progress(message);
}

function hideDialogLoading()
{
	myApp.dialog.close();
}

routes.js:

routes = [
    {
        path: '/',
		name: 'page-home',
        componentUrl: './components/page-home.html',
        keepAlive: true
    },
    {
        path: '/page-01/',
		name: 'page-01',
        componentUrl: './components/page-01.html',
        keepAlive: false
    },
    {
        path: '/page-02/',
		name: 'page-02',
        componentUrl: './components/page-02.html',
        keepAlive: false
    },
    {
        path: '/page-03/',
		name: 'page-03',
        componentUrl: './components/page-03.html',
        keepAlive: false
    },
    {
        path: '/page-04/',
		name: 'page-04',
        componentUrl: './components/page-04.html',
        keepAlive: false
    },
    // Default route (404 page). MUST BE THE LAST
    {
        path: '(.*)',
		name: 'page-404',
        componentUrl: './components/page-404.html',
        keepAlive: false
    },
];

page-home.html:

<!-- component template -->
<template>
    <div class="page stacked" data-name="page-home">
        
    <!-- NAVBAR -->
    <div class="navbar">
            <div class="navbar-inner sliding">
                <div class="left">
                    <a href="#" class="link icon-only panel-open" data-panel="left">
                        <i class="icon material-icons md-only">menu</i>
                    </a>
                </div>
                <div class="title">{{title}}</div>
            </div>
        </div>
        
        
        
        <!-- PAGE CONTENT -->
        <div class="page-content">
        
            <div class="card">
                <div class="list media-list chevron-center">
                    <ul>
                        <li>
                            <a href="#" class="item-link item-content">
                                <div class="item-media">
                                    <i class="material-icons">list</i>
                                </div>
                                <div class="item-inner">
                                    <div class="item-title-row">
                                        <div class="item-title">item-01</div>
                                    </div>
                                    <div class="item-text">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</div>
                                </div>
                            </a>
                        </li>
                    </ul>
                </div>
            </div>
        
            <div class="card">
                <div class="list media-list chevron-center">
                    <ul>
                        <li>
                            <a href="#" class="item-link item-content">
                                <div class="item-media">
                                    <i class="material-icons">list</i>
                                </div>
                                <div class="item-inner">
                                    <div class="item-title-row">
                                        <div class="item-title">item-02</div>
                                    </div>
                                    <div class="item-text">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</div>
                                </div>
                            </a>
                        </li>
                    </ul>
                </div>
            </div>
        
            <div class="card">
                <div class="list media-list chevron-center">
                    <ul>
                        <li>
                            <a href="#" class="item-link item-content">
                                <div class="item-media">
                                    <i class="material-icons">list</i>
                                </div>
                                <div class="item-inner">
                                    <div class="item-title-row">
                                        <div class="item-title">item-03</div>
                                    </div>
                                    <div class="item-text">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</div>
                                </div>
                            </a>
                        </li>
                    </ul>
                </div>
            </div>
        
            <div class="card">
                <div class="list media-list chevron-center">
                    <ul>
                        <li>
                            <a href="#" class="item-link item-content">
                                <div class="item-media">
                                    <i class="material-icons">input</i>
                                </div>
                                <div class="item-inner">
                                    <div class="item-title-row">
                                        <div class="item-title">item-04</div>
                                    </div>
                                    <div class="item-text">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</div>
                                </div>
                            </a>
                        </li>
                    </ul>
                </div>
            </div>
        
            <div class="card">
                <div class="list media-list chevron-center">
                    <ul>
                        <li>
                            <a href="#" class="item-link item-content">
                                <div class="item-media">
                                    <i class="material-icons">input</i>
                                </div>
                                <div class="item-inner">
                                    <div class="item-title-row">
                                        <div class="item-title">item-05</div>
                                    </div>
                                    <div class="item-text">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</div>
                                </div>
                            </a>
                        </li>
                    </ul>
                </div>
            </div>
        
        </div>
    </div>

</template>

<!-- rest of component data and methods -->
<script>
  // script must return component object
  return {
    data: function () {
      return {
        title: lblHome
      }
    },
    on: {
      pageInit: function () {
        var self = this;
      },
      pageBeforeIn: function () {
        var self = this;
        self.$app.panel.enableSwipe('left');
      },
    },
    methods: {
      helloWorld: function () {
        var self = this;
        self.$app.dialog.alert('Hello world!');
      },
    },

  }
</script>
<!-- component styles -->
<style>
.page-content .item-media
{
    padding-top: 0px;
    padding-bottom: 0px;
    margin-top: auto;
    margin-bottom: auto;
}

.page-content .item-media i
{
    font-size: 40px;
    width: 40px;
    height: 40px;
}

</style>

Thanks, Fabricio.


#2

Можете привести пример где “posted in the forum, github, stackoverflow”? Я помню только тикет на GitHub, там говорилось о Vue и утечках памяти. Автор Ф7 вроде все решил, кроме событий в input. Посмотрите: https://github.com/framework7io/framework7/issues/2848


#3

Hi shastox, thanks for your response.

I have seen that thread, other related of the most recent:



I leave an image of memory consumption on an Android device as I navigate:

Each jump is a new screen that I visualize and that is what I want to avoid. Even the memory consumption increases if I navigate to a screen I have visited before.

Do your applications have the same behavior?


#4

Framework7 has great memory management by itself. It tries its best to clear all traces of events and instances. But it doesn’t mean that memory leaks couldn’t happen, because:

  • you may have them in your custom code (most of cases)
  • they may happen in 3rd party libs, e.g. in Vue.js

Some advices:

  • Don’t use keepAlive and stackPages for better memory management, especially if you have a lot of DOM there
  • Don’t use html event handlers, and you do use them, e.g. on attributes in HTML
  • Detach all events handlers that you assign in pages and destroy instances in page’s beforeRemove event or Vue’s beforeDestroy hook
  • Use latest F7, e.g. 4.x.x

#5

Well, it’s clear @nolimits4web .

Thanks for the details, i will keep them in mind to add it to my App.

For information purposes, another point to keep in mind: cordova-android version.
I have run tests with [email protected] and [email protected] (same plugins, same App F7-V3, exactly same navigation path) and the performance has improved a bit, then memory performance images:
[email protected] - 375,5MB

[email protected] - 325,4MB (≈ 50MB less):

Thanks again. Your work is incredible.

Regards, Fabricio.


#6

Поясните, пожалуйста. Ваше приложение / сайт на Framework7 занимает 300 мегабайт в оперативной памяти?


#7

That I understand and that is what appears in the Android Studio Memory Profiler although it seems a lot.

Could you debug one of your applications and tell me if they handle those numbers?

On the other hand, if I take a memory snapshot from Chrome Inspect the values thrown are around 2MB (see screenshot). It’s a bit confusing, I really do not know.

What I’m sure of is that the consumption of memory is increasing, resulting in the App Crash in devices with few features.