Sunday 29 April 2012

The Ultimate Answer to Titanium memory leaks




The question


What are memory leaks and should Titanium programmer worry about memory leaks?


The answer to that question is as usual - it depends.


Memory leaks are situations when allocated memory in applications grows in uncontrolled way and never gets released. In some situations that can lead to application slowdown and crash.


Memory allocation and deallocation in JavaScript environments is managed automatically by garbage collection mechanism and programmers, in theory, should not worry about memory. In practice, there are situations where programmer should worry about memory handling.

JavaScript by itself has no memory problems, it's always interaction of native and host objects, or host objects themselves, that cause memory leaks. Titanium, as JavaScript host environment, is not different, but it has its own specifics.


One thing that can cause memory leaks in Titanium are application-level event listeners: Ti.App.addEventListener, "location", etc. That kind of listeners remain in memory until application runs (or until they are removed) and in situations where they are being added multiple times (by programer's error), the application will eventually crash.
Sometimes programmers use app-level events for communication between different UI components and in that cases memory leaks can happen because components will be "trapped" in app-level event. They are not like component-level events that are removed when component is removed from memory (usually, when window closes).


The problem


There are more situations that can potentially cause memory leaks. No one is sure in what situations cause memory leaks in Titanium (situations vary and are very specific) but it seems that similar things from other JavaScript environments apply in Titanium also (closures, listeners, circular references). In some cases it's Titanium's fault, but sometimes it is just lack of understanding.


Let's say that "app.js" contains this:

function createViews() {
    var views = [];
     for (var i = 0; i < 100; ++i) {
         views.push(Ti.UI.createView());
    } 
}
createViews();

Should views be freed from memory?

There was actually a bug filled with this kind of situation as memory leak situation. The problem here was not in Titanium, but in understanding (and Android emulator :) ). Some people suggested that "views" variable should be set to "null". Some suggested that all items in "views" array should be set to "null". But nothing is necessary here (and it shouldn't be) because "views" array is local variable in scope created by "createViews" function that will be destroyed after that function finishes with execution. Setting a variable to "null" in this case has no sense (as it has been shown in that bug report).

Setting a variable to "null" comes from web development, where in some cases (in older versions of IE primarily) interaction between host and native objects caused memory leaks and setting a variable to "null" would solve memory issues. But we should not be blind and use that technique everywhere without understanding the problem because it is not guarantied that setting a variable to "null" will solve memory issue or that setting variable to "null" is really necessary. We, as web developers, must deal with memory leaks in older versions of IE just because it is still used by many people. As Titanium developers we are in much better situation and we can report memory leak issue and wait until it's fixed (usually memory leak issues are fixed soon) and release our application without ugly workarounds.



The solution


Understanding.


The key of finding memory leak situations and solution of that problem is understanding. Titanium environment is one implementation of ECMAScript standard and therefore it should obey to the rules defined by that specification. I'll try to explain some basic concepts.

Scope is part of the program in which variables are bound to the values (variable is always declared with "var" keyword). We usually say that variable references value, but it would be better to say that variable identifies value (to not mix terminology with references from C-like programming languages). So, variables are not true references and we don't deal with low level things, but rather with higher level abstraction.


There is one global scope and new scopes are created by functions. When new function is defined, a reference to parent scope is stored (only global scope has no parent scope). That's why scopes are static (lexical), but variables and their values are resolved dynamically:

//app.js
//global scope 
this; //global object
this.Ti; //Titanium object

//cool so far :)
//let's declare a variable

var obj = { prop: 'abc' }; //created new object with string property
this.obj === obj; //true, variables defined in global scope are properties of global object
hm = 42;
this.hm === hm; //true, when no "var" is used, 
//property of global object is accessed if value is set, 
//reference exception is thrown otherwise

var oneMoreObj = obj; //created one more identifier to the object

function createNewScope(destroy) {
    destroy = null;
    var something = 2; 
    Ti.API.info(obj.prop);
}
createNewScope(obj); // abc

obj.prop = 'cba';

createNewScope(oneMoreObj); //cba


What happened here?

As I said, functions upon creation store a reference to the scope in which they are created. When they are executed, they create list of their own local variables, inner functions and formal parameters (we say that variables are "hoisted" to the top of the scope). When some variable is accessed during execution, it is resolved by lookup in list that current scope contains and if it is not found, it's searched in similar way in parent scopes (until global scope is reached). That's how "obj" variable is resolved inside "createNewScope" - variable is not found in current scope and it is searched in parent scope in which it's defined.
Note that "destroy" variable does not actually destroy outside object (although, modification would change it) because "destroy" is just one more identifier and setting it to "null" is just identifies different value (it doesn't change what "obj" identifies).
When function finishes with execution, local variables ("something" in this case) are destroyed in most cases. But there are cases when function has finished with execution, but scope and its variables are not destroyed. Functions are first-class citizens and they can be stored in variables, returned from function, etc. Because of that and the fact that scopes are static (they contain a reference to scope in which they are defined), they are not closed when they are finished with execution in cases when some child scope uses variables from parent scope (it forms so called closure). For example:


function createFunc() {
    var test = "Closure"; 
    return function() { alert(test); } 
}

var func = createFunc();
//createFunc has finished with execution, but we can still get "test" variable
func();


Sometimes that can lead to strange effects.


for (var i = 0; i < buttons.length; ++i) {
    buttons[i].addEventListener('click', function() {
        alert(i);
    });
}

This use-case usually produces WTF-moment because click of any button alerts same value. What happened here ... "buttons" (as identifiers of host objects) will hold event listener function. That function will contain a reference to parent scope. The point here is that all listener functions will contain a reference to same parent scope and that at the time button was actually clicked, "for" loop has finished with execution (and "i" variable will have same value it had at the end of the loop). To avoid that, closure can be created that captures value of "i" variable in each loop:


for (var i = 0; i < buttons.length; ++i) {
    buttons[i].addEventListener('click',
        (function(currentI) {
            return function() {
                alert(currentI);
            }; 
         })(i)
    );
}


The trick here is creation of self-invoking function. Functions can be anonymous and they can be invoked upon creation. To create anonymous function:


function() {}


To invoke it:


(function() {})()


Note that value of "i" is formal parameter to self-invoking function and because of created closure, parent scope of event listener function (scope created by anonymous function) won't close itself until button is removed from memory (along with reference to listener function).
In Titanium, button should be removed from memory when window closes and when nothing else identifies it from JS. Current documentation, in my opinion, does not explain that precise enough.


win.remove(view);  // view & button still exist
view = null; // deletes the view and its proxy, but not the button!


To be precise, "win.remove" should remove view's visual representation on the screen. When it is not present on the screen and nothing identifies it from JS, it should be removed from memory (otherwise, it's memory leak situation by definition). Setting variable to "null" does not delete the view directly (as it may sound from the comment from docs), the point here is that "view" variable now identifies to different value and Titanium host object "TiUIView" is not identified from JS anymore and therefore should be marked for garbage collection sometimes in the future. We could also set "view" to 42 (or anything else), it would not make a difference. If "view" variable is created in scope that is not global, it should be marked for garbage collection when that scope closes (remember bug report above). That's why, the best is to avoid using global scope except for function definition and some other controlled use cases.

So why to set a variable to "null" (or 42 :) ). In some cases (closures, circular references), when garbage collection mechanism misses to detect objects that it needs to deallocate, that can help. But it is not guaranteed that will help. As I said, it's important to understand the problem so it can be solved. Hope this article helps that cause. :)