Garbage Collection in the UcompOS RPF

OscarTheGrouch In large and sophisticated Flash-based applications, such as the Rich Portal Applications you develop with the UcompOS Rich Productivity Framework, Garbage Collection becomes an increasingly important consideration.

Garbage Collection is a computer science concept that deals with memory management and re-allocating memory occupied by objects that are no longer needed or in use by the computer program.

Garbage Collection in a large-scale event-driven Flash architecture is a crucial consideration and unfortunately, can also be a tedious proposition to properly implement.

One of the more important considerations in aiding with Garbage Collection in Flash-based application is proper management of event listeners.

In a best-practice implementation, any call to addEventListener() would be followed up with a call to removeEventListener() once the event listener was no longer needed. Event listeners perpetuate the objects they are associated with so even once a particular object is destroyed, that doesn’t necessarily mean any event listeners associated with it will be destroyed too.

Failure to remove event listeners once they are no longer needed in a large-scale implementation can cause memory leaks and performance can break down over time.

In all your Flash applications, you should employ this strategy when and where possible.

In a typical large-scale UcompOS RPF implementation, Flash-based applications and sub-applications are continuously being loaded and unloaded into the UcompOS Portal.

The concept of Proxy Components in the UcompOS RPF involves event listeners being attached to a Proxy Component in one entity that represents an object in another UcompOS entity and then listening for dispatched events.

Therefore, the concept of garbage collection takes on especially critical importance in the UcompOS RPF because if UcompOS applications are being loaded and unloaded constantly and the event listeners that are attached to them are not being properly removed, performance would become an issue in a large-scale implementation.

The UcompOS Portal has been designed such that when SWF content that has been loaded into the UcompOS Portal is unloaded, a garbage collection routine is implemented to try to optimize system performance. (The three ways SWF content is loaded into the UcompOS Portal is in UcompOS Window instances, UcompOS Artifact instances, or into the UcompOS run-time.)

As of the UcompOS RPF Public Alpha release 0.4.6, some additional enhancements to the garbage collection process have been implemented that I will discuss here.

Understanding the UcompOS RPF garbage collection architecture can help you build more scalable Rich Portal Applications.

The recent enhancements come in the realm of Proxy Components.

By strict convention, all Proxy Components must extend the SDK class AbstractProxyComponent. As of UcompOS RPF 0.4.6, AbstractProxyComponent now extends a new SDK class called SDKEventDispatcher which extends EventDispatcher.

(Also, as a side-note, the AbstractProxyComponent class now implements IEventDispatcher in addition to IProxyComponent so you can refer to your Proxy Components as IEventDispatcher or IProxyComponent instances interchangably.)

SDKEventDispatcher simply overrides addEventListener() and offers a protected property _listeners of type Array.

I chose to create this separate class versus simply overriding addEventListener() in AbstractProxyComponent so that in the event the new clean-up process employed can be useful outside the scope of a Proxy Component, one could simply extend SDKEventDispatcher versus EventDispatcher.

Here is the over-ridden addEventListener():

override public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void
{
	_listeners.push({type:type,listener:listener});
	super.addEventListener(type,listener,useCapture,priority,true);
}

Notice that I am always assigning a true value to the useWeakReference parameter in the super call to addEventListener(). This is because in the context of attaching event listeners to Proxy Components, you always would want them to be weak references to aid in garbage collection.

In AbstractProxyComponent, there is a new protected method, cleanup() which is as follows:

protected function cleanup(event:SDKEvent=null):void
{
	while(_listeners.length)
	{
		var object:Object = _listeners.pop();
		removeEventListener(object.type,object.listener);
	}
	_sdkModel.removeProxyComponent(this.uuid);
	var commandObject:CommandObject = new CommandObject("SDK.cleanup",{uuid:this.uuid});
	var dataPackage:DataPackage = new DataPackage(Utilities.createUID(),_sdkModel.connectionId,this.destination,commandObject);
	_sdkClient.send(dataPackage);
}

What the cleanup() method does is it automatically removes any event listeners that have been attached to the Proxy Component.

It also sends a message to the end-point of the Proxy Component targeting its SDK.cleanup public API method that is built into the UcompOS SDK and therefore exposed by any UcompOS entity. This aids in UcompOS RPF garbage collection-related activities.

Calls to cleanup() do not occur automatically.

When you would call cleanup() in a Proxy Component would be an implementation specific consideration.

For instance, with these new capabilities, I have updated some of the UcompOS Portal Proxy Components that are built into the UcompOS SDK.

Consider the updated constructor of the UcompOSWindowProxy class:

public function UcompOSWindowProxy()
{
	super();
 
	// set the destination property to the UcompOS Portal
	super._destination = _sdkModel.root;
	<strong>addEventListener(CLOSE,cleanup);</strong>
}

The call to addEventListener(CLOSE,cleanup) is the new addition to the constructor.

What it does is it causes the inherited cleanup() method to always be called when the UcompOS Window instance on the UcompOS Portal that is represented by the UcompOSWindowProxy instance is closed.

Therefore, any event listeners you have attached to the UcompOSWindowProxy instance are automatically removed whenever the window is closed.

In 100% of the possible use cases for the UcompOSWindowProxy, this is the desired behavior.

The net result of this is you don’t have to worry about making your own calls to removeEventListener() for your particular UcompOSWindowProxy instances.

The same sort of behavior has been engineered into some of the other Proxy Components built into the UcompOS SDK including UcompOSAlertProxy, UcompOSDialogueProxy, UcompOSArtifactProxy, and UcompOSBrowserWindowProxy.

Consider this strategy when you are building your own Proxy Components and remember to always use removeEventListener() in all your other Flash applications whether you are deploying them in the UcompOS Portal or not.

Self-Referencing Proxy Components

The AbstractProxyComponent class in the UcompOS SDK must, by strict convention, be the superclass for all Proxy Components. Understanding the mechanics of AbstractProxyComponent and its public properties and methods is a crucial step in building the most powerful Proxy Components that promote richer and more engaging experiences for users of your Rich Portal Application.

In this post, I will touch on one of the public properties of AbstractProxyComponent, self, and explain its purpose and provide a use-case scenario.

As you know, in the context of the UcompOS Continuum, the UcompOS Portal is simply one of X UcompOS entities, each of which have the UcompOS SDK implemented.

The UcompOS Portal sponsors a host of public API methods and the UcompOS SDK has been equipped with a batch of Proxy Components that serve as clients that can be used in your own UcompOS Applications to connect to these public API methods.

At the current time, the self property on AbstractProxyComponent may seem tightly coupled to the UcompOS Portal’s Proxy Components and specifically, to two of those Proxy Components: UcompOSWindowProxy and UcompOSArtifactProxy. However, as the UcompOS RPF continues to expand, the self property on a Proxy Component will take on increasingly significant meaning.

Right now however, the UcompOSWindowProxy and UcompOSArtifactProxy provide a very good model with which to illustrate the purpose of this self property.

As you know, when you create an instance of UcompOSWindowProxy and call its add(); method, you are launching a UcompOS Window on the UcompOS Portal and you are specifying the URL of the content that should be loaded into this window.

Behind the scenes, when the UcompOS SDK loads this content to the UcompOS Window, it passes the content some crucial information that that content will use to become a member of the UcompOS Continuum thereby being able to participate in SDK transmissions with other entities.

Here’s an interesting question:

What if you load some content into a UcompOS Window, and then you want to reference the UcompOS Window that the content is loaded into from the content itself?

The answer to this is you would create a UcompOSWindowProxy instance and then set its self property to true.

As an example of how I have used this capability, I have created a Rich Text Editor for my Educator 2 application I am building on top of the UcompOS RPF. This editor is a Flex 4 implementation.

In Educator 2, by strict convention, all SWF content I load into UcompOS Window and UcompOS Artifact instances is loaded in sandboxed configurations and I rely 100% on SDK transmissions for communication with other entities (the exception is drag and drop which I talked about in this posting).

The challenge then becomes, how do I resize the editor? To resize the editor, the user isn’t going to actually resize the editor itself, rather, the UcompOS Window instance the editor is loaded into. Since my content is sandboxed, I can’t access the parent container the content is loaded into using traditional scripting from within my Editor application.

The solution is to create a reference to a UcompOSWindowProxy and set its self property to true.

Let’s take a look at some code here that is similar to what you’d see in my editor application:

private function init(event:FlexEvent):void
{
 
	// Instantiate the UcompOS SDK
	SDKClient.getInstance(this,new ServicesDictionary());
 
	// Create a UcompOS Window Proxy instance
	var w:UcompOSWindowProxy = new UcompOSWindowProxy();
 
	// Create a self-reference - w in this case becomes a reference to
	// the UcompOS Window instance the content in scope is loaded into
	w.self = true;
 
	// listen for window resize events
	w.addEventListener(UcompOSWindowProxy.RESIZE_END,resizeHandler);
	w.addEventListener(UcompOSWindowProxy.MAXIMIZE,resizeHandler);
	w.addEventListener(UcompOSWindowProxy.RESTORE,resizeHandler);
 
}
 
// the SDKEvent instance dispatched to this handler following
// a window resize operation contains the new width/height of the
// UcompOS Window - the width and height of the content viewing
// portions of the UcompOS Window are housed in the event.data.width
// and event.data.height properties
private function resizeHandler(event:SDKEvent):void
{
 
	var w:UcompOSWindowProxy = event.target as UcompOSWindowProxy;
 
	// suppose my editor component is a variable named myEditor
	myEditor.width = event.data.width;
	myEditor.height = event.data.height;
 
}

These same mechanics can be leveraged in UcompOSArtifactProxy instances.

In fact, in one of my earliest UcompOS Video Tutorials entitled Building a UcompOS Weather Channel Widget, we saw this technique.

In that implementation, I created a self referencing UcompOSArtifactProxy instance so I could attach a Context Menu to the UcompOS Artifact and capture user selections on this Context Menu.

Here is a very simple example of a Flash CS4/CS5 UcompOS Application that leverages this principle:

package
{
 
	import flash.display.Sprite;
	import com.ucompass.ucompos.sdk.SDKClient;
	import com.ucompass.ucompos.sdk.proxycomponents.UcompOSArtifactProxy;
	import com.ucompass.ucompos.sdk.events.SDKEvent;
 
	public class Example extends Sprite
	{
 
		public function Example()
		{
 
			var sdkClient:SDKClient = SDKClient.getInstance(this);
 
			var artifact:UcompOSArtifactProxy = new UcompOSArtifactProxy();
			artifact.self = true;
			artifact.setContextMenu(new XML(""));
			artifact.addEventListener(UcompOSArtifactProxy.MENU_ITEM_SELECT,contextMenu_handler);
 
		}
 
		private function contextMenu_handler(event:SDKEvent):void
		{
			trace("User selected "+event.data.label);
		}
 
	}
 
}