Memory

Let’s say it one more time: the memory on mobile devices is limited. If you exceed it, Android will terminate your application.

Creating Objects

Choose the appropriate object. For instance, use a Sprite instead of a MovieClip if you don’t need multiple frames. Use a Vector, which is more efficient than an Array, to store objects. Call the getSize() function to determine the memory footprint for a particular data type.

Allocating new blocks of memory is costly. Create your objects at an idle time, ideally when your application first initializes, and reuse them throughout your application.

Object pooling creates a pool to store objects and re-use them over time. In this example, we pop an object, or create one if none exists, and then push it back when done until the next use:

[code]

import flash.display.Sprite;
var pool:Vector.<Sprite>();
// get a new Sprite
var sprite:Sprite = popSprite();
sprite.init(); // set properties as needed
addChild(sprite);
function popSprite():Sprite {
var sprite:Sprite;
if (pool.length > 0) {
sprite = pool.pop(); } else {
sprite = new Sprite();
}
return sprite;
}

[/code]

When the object is no longer needed, return it:

[code]

removeChild(sprite);
pushSprite(sprite);
function pushSprite(sprite:Sprite):void {
pool.push(sprite);
}

[/code]

This approach is organic. The application creates objects as needed until it reaches a sufficient quantity to recycle them. Another method is to create all the objects initially, up to a maximum amount.

Removing Objects

Memory is dynamic. As objects are deleted, memory is released via a process called garbage collection.

Only primitive types, such as String, Number, and Boolean, are used directly. All other types are used using a reference, which must be removed to clear them from memory. This includes removing a child from a displayList, splicing an Array array or a Vector, stopping and deleting a Timer, removing an EventListener, or nulling a reference from another object.

Once this is all done, set the object to null to help the garbage collector work more quickly.

Use the disposeXML method for an XML object. It traverses the tree and sets all pointers between parents and children to null to make the object available for immediate garbage collection:

[code]

import flash.system.System;
function onXMLLoaded(event:Event):void {
var xml:XML = event.target.data;
// sudo code to parse and store data
var dataStorage = parseXML(XML);
System.disposeXML(xml);
xml = null;
}

[/code]

Use the dispose method for a BitmapData object:

[code]

var bitmapData:BitmapData = new BitmapData(480, 800);
bitmapData.dispose();
bitmapData = null;

[/code]

One of the principal reasons for memory leaks is lingering event listeners. A common recommendation today is to set weak event listeners as shown in the code below. Weak references are not counted by the garbage collector as references, a topic we will cover next:

[code]

var sprite:Sprite = new Sprite();
// strongly referenced listeners
sprite.addEventListener(MouseEvent.CLICK, onClick);
// weakly referenced listeners
// eventName, listener, capturePhase, priority, useWeakReference
sprite.addEventListener(MouseEvent.CLICK, onClick, false, 1, true);

[/code]

Use this approach with caution. Too many developers tend to rely on it instead of removing listeners in code. As a responsible coder, you should be diligent about doing the proper housekeeping.

If your code is large, or difficult to maintain, you can create an array to store listeners or a mechanic to automate their removal.

Create a destroy method to use in your classes to remove listeners and objects. To enforce the practice, use an IDestroy interface in which all classes need to have the destroy method but can implement it according to their individual needs:

[code]

// interface
public interface IDestroy {
function destroy():void;
}
// class A
public class ClassA implements IDestroy {
public function ClassA() {
}
public function destroy():void {
// remove listeners
// clear display list
while (numChildren > 0) {
removeChildAt(0);
}
}
}
// class B
public class ClassB implements IDestroy {
public function ClassB() {
}
public function destroy(event:):void {
// empty arrays
// set variables to null
// call garbage collection
System.gc();
}
}

[/code]

If one of your classes implements the interface but erroneously does not have a destroy method, you will see the following error:

[code]

Interface method destroy in namespace com:IDestroy not implemented by class
com.ClassC

[/code]

Garbage Collection

Garbage collection is an automatic mechanism that deallocates the memory used by objects when they are no longer referenced in the application. It is important because it releases memory for future use, and particularly so for mobile devices with limited memory.

The garbage collector is not predictable, but you can sometimes monitor it by watching your memory consumption using the System class. totalMemory refers to the portion of memory allocated to your code. privateMemory is the entire memory consumption of the application. All functions return bytes; divide by 1,024 to obtain kilobytes:

[code]

import flash.system.System;
System.privateMemory/1024;
System.totalMemory/1024;
System.totalMemoryNumber/1024 // returned as NUMBER for greater precision
System.freeMemory/1024; // memory unused

[/code]

You can call the garbage collector directly in AIR, but it requires significant memory and can result in a noticeable slowdown in the application. For this reason, reuse objects or clean them up quickly and frequently as described earlier:

[code]

System.gc();

[/code]

The AIR runtime uses two different methods for best results.

Reference count keeps track of the object and marks it for deletion when it has a value of zero if it has no reference:

[code]

var a:Object = new Object(); count is 1 for a;
var b:Object = a; 2 for b;
a = null; 1 for a;
b = null; 0 for b;

[/code]

This method fails when two objects reference each another:

[code]

var a:Object = new Object(); is 1 for a;
var b:Object = new Object(); is 1 for b;
a.b = b; 2 for b;
b.a = a; 2 for a;
a = null; 1 for a;
b = null; 1 for b;

[/code]

Mark sweeping handles this issue in a more accurate way but takes longer and does not happen as often. It traverses the entire application tree and marks an object that has a reference, marks its children, and so on, recursively. Objects that are not marked can be assumed to be eligible for collection.

In AIR for Android, the garbage collector is more iterative and objects are collected more frequently because memory is in short supply. For this reason, be attentive to making sensors and objects with listener member variables, not local variables.


Posted

in

by

Tags: