YOUR FEEDBACK
Gregor Rosenauer wrote: well, not what's your take on this? Did I miss a second page of this article or...
AJAXWorld RIA Conference
Early Bird Savings Expire Friday Register Today and SAVE !..

SYS-CON.TV

2008 East
DIAMOND SPONSOR:
Data Direct
Frontiers in Data Access: The Coming Wave in Data Services
PLATINUM SPONSORS:
Red Hat
The Opening of Virtualization
Intel
Virtualization – Path to Predictive Enterprise
Green Hills
IT Security in a Hostile World
JBoss / freedom oss
Practical SOA Approach
GOLD SPONSORS:
Software AG
The Art & Science of SOA: How Governance Enables Adoption
PlateSpin
Effective Planning for Virtual Infrastructure Growth
Fujitsu
Automated Business Process Discovery & Virtualization Service
Ceedo
Workspace Virtualization
Click For 2007 West
Event Webcasts

2008 East
PLATINUM SPONSORS:
Appcelerator
Think Fast: Accelerate AJAX Development with Appcelerator
GOLD SPONSORS:
DreamFace Interactive
The Ultimate Framework for Creating Personalized Web 2.0 Mashups
ICEsoft
AJAX and Social Computing for the Enterprise
Kaazing
Enterprise Comet: Real–Time, Real–Time, or Real–Time Web 2.0?
Nexaweb
Now Playing: Desktop Apps in the Browser!
Sun
jMaki as an AJAX Mashup Framework
POWER PANELS:
The Business Value
of RIAs
What Lies Beyond AJAX?
KEYNOTES:
Douglas Crockford
Can We Fix the Web?
Anthony Franco
2008: The Year of the RIA
Click For 2007 Event Webcasts
TOP THREE LINKS YOU MUST CLICK ON


Real-World AJAX Book Preview: JavaScript Profiling
Real-World AJAX Book Preview: JavaScript Profiling

This content is reprinted from Real-World AJAX: Secrets of the Masters published by SYS-CON Books. To order the entire book now along with companion DVDs for the special pre-order price, click here for more information. Aimed at everyone from enterprise developers to self-taught scripters, Real-World AJAX: Secrets of the Masters is the perfect book for anyone who wants to start developing AJAX applications.

JavaScript Profiling
In some cases you may not know precisely where a performance bottleneck lies. Using our technique of surrounding suspected bottlenecks with timestamps won't work in these cases. We need a way to get a broader overview of application performance. Fortunately there are a number of profiling tools. Unfortunately, they're not standard across browsers.

One of the best tools is the Venkman JavaScript Debugger from Mozilla. It's available at www.mozilla.org/projects/venkman/. Venkman has a profiling option that lets you capture profiling data, then save it and analyze where your application spent its time. Simply select the menu Profile->Collect Profile Data and use your application. Then, when you're finished gathering your profiling data, select the menu Profile->Collect Profile Data to stop collecting data, then Profile->Save Profile Data to finish. You can also click the clock icon to turn profiling on or off. When profiling is enabled, the clock icon will have a green check mark on it. To restart profiling, select the menu Profile->Clear Profile Data.

When profiling is enabled in Venkman, it will collect the number of calls and the maximum, minimum, and average call times for every function called in your application. If you want to restrict profiling, clear or save profile data to particular JavaScript files, you simply select those scripts or functions in the Loaded Script area before selecting the appropriate Profile menu item.

The supported data formats for saving are HTML, Text, CSV, and XML. Specify the format with the file extension of the saved file. Figure 9.2 shows the saved profile data in HTML form (I've used the dot notation lookup performance test as an example):

The profile output may look cryptic at first, but once you get the hang of it, you can track down hotspots in short order. Specifically, for each function profiled, look at the number of calls and the total time in milliseconds spent in each function. Find the big culprits and drill down into where those functions are spending their time - they may just be wrappers for other more wasteful functions. Ignore the small stuff. There's no need to optimize functions that are hardly ever called and take up no CPU time.

In IE, your best bet for profiling is the commercial Tito Web Studio JavaScript Profiler (www.titosoftware.com/profiler.php). It provides functionality similar to Venkman.

Memory Optimization
Until recently, we didn't really concern ourselves with memory consumption and garbage collection in JavaScript. In traditional Web applications, each page load wipes the JavaScript memory slate clean. If there were memory leaks, usually that was someone else's problem - the poor Web developer whose page loaded at the end of a long day of browsing and consequently ran slow as molasses. Users typically knew enough to exit and restart their browsers in any case, starting fresh without any memory leaks.

Because of the clean memory slate that came with each page load, memory management and garbage collection is one of the least stress-tested areas of browsers - in short, a weak link. With AJAX, more pages will stay in memory longer, and some single page applications will stay in memory for hours or even days, putting the spotlight squarely on this weakness. This fundamental change in how Web applications make use of the browser means we have to pay special attention to memory management issues, reducing unnecessary allocation of objects and avoiding memory leaks.

One of the more common ways of leaking memory is through accidental closure. What is a closure? Without getting into too much detail, when you execute a function, it creates an "execution context," where all of the local variables and other objects created or accessed during the execution of that function reside or are referenced.

function example() {
    // num exists in the execution context of example()
    var num = 5;
}

Each execution of the function creates a new context. When the function returns, all of the memory used up by that execution context is reclaimed. The only time that the memory isn't reclaimed is if a reference to a function created in that execution context becomes accessible outside of the function.

function example2() {
    // num exists in the execution context of example()
    var num = 5;

    function innerFunction() {
      num = num + 5;
      return num;
    }

    return innerFunction;
}
var funcRef = example2();

In the example above, the inner function has to access the variable num in that invocation of example2's execution context. That means that num, and any other memory resident objects created in the execution context, stays in memory until the reference to the inner function is garbage collected. This behavior is part of the specification of ECMAScript and can be quite powerful when used properly, but can cause all manner of memory leaks when used improperly. It's actually quite easy to create an accidental closure.

The best way to address the problem of these memory leaks is to be vigilant when returning functions from within a function call. That happens quite a bit if you're assigning anonymous event handlers to a DOM object inside of a function call. Just be aware that any variable declared in or available as a parameter in the surrounding function will live on as long as the inner function is around, so don't declare huge arrays or objects needlessly.

A more serious type of memory leak in IE comes from assigning event handlers to a DOM object (or any COM object, for that matter). It's a sure way to leak memory permanently in IE. Take for example the following function that inserts a clickable div into the page:

function insertClickableDiv() {
    var elem = document.createElement("div");
    elem.innerHTML = "click me";
    elem.onclick = function() { elem.className = "boldDiv"; };
    document.body.appendChild(elem);
}

This creates a closure, since we assign a function created inside the outer function to a DOM element outside of its scope. Further, the DOM element, which is a part of the closure, refers to the anonymous inner function, creating a circular reference. By circular reference, I mean that two objects refer to one another. In this case, the function's execution context refers to the DOM element and the DOM element refers to the function via its onclick event handler.

Normally, good automatic garbage collection algorithms will handle circular references, but in IE, COM objects are reference-counted. That means that the browser keeps track of how many references there are to an object. In the case of a circular reference, none of the participating objects ever have a reference count of zero, so they stay in memory for good even after a page load.

There is a tool for IE called Drip that detects these sorts of memory leaks (http://outofhanwell.com/ieleak/index.php?title=Main_Page). To track down memory leaks, start up the Drip browser and load the application you want to test, exercise its functions, then click the "Show DOM Leaks" button. You'll see a list of the leaked DOM elements.

To track down where your leaks are in a large body of code, you can add an ID attribute to each DOM element, perhaps using the name of the enclosing function or method as a root. If you see lots of DOM elements whose IDs are all of the form "insertClickableDiv_123", then you'll know where to start looking.

Once you've identified the source of the problem, you can fix it by making sure the event handler function isn't in the execution context of the created DOM element. One way to do this is to use external functions. A better way is to wrap the DOM element creation into its own execution context one level down:

function insertClickableDiv_NoLeak(i) {
    var changeToBold = function() { this.className = "boldDiv"; };
    (function(){
      var elem = document.createElement("div");
      elem.innerHTML = "click me";
      elem.onclick = changeToBold;
      document.body.appendChild(elem);
    })();
};

The changeToBold function is external to the anonymous function that we create and call all in one step. Since there's now no reference from the execution context of the event handler, we've broken the circular reference and things should be okay.

Object Pooling
Memory leaks can make an application sluggish, but heavy memory consumption, even in the absence of leaks, can plague complex object-oriented applications. That's true of Java and C#, not just JavaScript. One approach to handling the creation of expensive resources is pooling. You see this quite a bit in applications with database connections, which are expensive to create. The basic idea is that you have a pool of reusable resources ready to go. When an application asks for a resource from the pooling subsystem, an instance is pulled from the pool and returned. When the application is done with the resource, it returns it to the subsystem and it's put back in the pool. If we run out of available instances, we can do any number of things: we can force the application to wait until an instance becomes available or we can create an instance on-the-fly. The management of resource pools is a subject unto itself. Look at the implementation of Apache server pools and JDBC connection pools for some examples of how this management is done.

We can apply this pooling principle to any run-of-the-mill JavaScript object, but it makes the most sense to apply it to our most expensive resources. The XMLHttpRequest object can be pretty expensive. And if we make hundreds or thousands of requests over the lifetime of an application, it makes sense to reuse them. The key, of course, is the term reusable. The XMLHttpRequest can have some reusability problems in IE, if you don't use it correctly. The trick is in the order you issue the open() method call and assign the onreadystatechange listener.

// reusable in all browsers except IE
xhr.onreadystatechange = function() {
    // process result
};
xhr.open("GET", url, true);
xhr.send("");

// reusable in all browsers including IE
xhr.open("GET", url, true);
xhr.onreadystatechange = function() {
    // process result
};
xhr.send("");

Always call the open() method first, then assign the event listener. Once we have this little trick under our belts, we can create our XHR pool.

Listing 9.2

var XHRPool = (function(){
    // static private member
    var stack = new Array();
    var poolSize = 10;

    var nullFunction = function() {}; // for nuking the onreadystatechange

    // private static methods

    function createXHR() {
      if (window.XMLHttpRequest) {
      return new XMLHttpRequest();
      } else if (window.ActiveXObject) {
      return new ActiveXObject('Microsoft.XMLHTTP')
      }
    }

    // cache a few for use
    for (var i = 0; i < poolSize; i++) {
      stack.push(createXHR());
    }

    // shared instance methods
    return ({
      release:function(xhr){
        xhr.onreadystatechange = nullFunction;
      stack.push(xhr);
    },
    getInstance:function(){
      if (stack.length < 1) {
        return createXHR();
      } else {
        return stack.pop();
      }
    },
    toString:function(){
      return "stack size = " + stack.length;
      }
    });
})();

Note that this implementation depends on good programmer behavior. If you don't release the resource in the end, it will never make it back into the pool. Use of the pool is straightforward. You just have to remember to call XHRPool.release() in the onreadystatechange handler.

function testXHRPool(url) {
    var xhr = XHRPool.getInstance();

    // do the operation
    xhr.open("GET", url, true);
    xhr.onreadystatechange = function() {
      if (xhr.readyState==4) {
        // if "OK"
        if (xhr.status==200) {
            // process result
          }
        XHRPool.release(xhr);
      }
    };
    xhr.send("");
}

Using our timing function from before to create 100 XMLHttpRequest objects 500 times, this change resulted in more than a 50% speed improvement over a straight XMLHttpRequest creation in Firefox, Opera, and Safari, and a more than a 95% improvement in IE6.

This content is reprinted from Real-World AJAX: Secrets of the Masters published by SYS-CON Books. To order the entire book now along with companion DVDs, click here to order.

About Dietrich Kappe
Dietrich Kappe is a co-founder and the CTO of Pathfinder Development, a firm that combines User Experience Design and Agile to speed software product development. He published one of the first 100 public websites and launched one of the first Java Servlet-based web applications. He has been a software engineer for two decades, a frequent open source contributor, and has developed applications for the Media, Financial Services, Insurance and Healthcare industries.

LATEST AJAXWORLD RIA STORIES
Rich Internet Applications have become ubiquitous to the point that many of us rely on them for daily activities. But there's a big difference between consumer websites and enterprise-class applications. While new browser capabilities provide a richer look and feel for consumer s...
Cloud Computing isn’t just another buzzword: this session will look at what the industry is up to, Amazon is up to, and especially how people are innovating in the cloud. Buzzwords aside, virtualized (cloud) computing is a disruptive game changer at both technical and business ...
The Dojo Toolkit is an open-source JavaScript toolkit that has a large community following in and out of the Enterprise. One of the many useful aspects of Dojo is the ability to extend the toolkit to incorporate new functionality. dojo.E is a set of extensions for dojo 1.1 that m...
Using AJAX and Comet, this presentation walks through the process of creating a simple tic-tac-toe game in which two people play while other people can watch the game via their browser. The session involves creating a simple game playable in one session and then stepping through ...
Web applications are accessible on smart phone, TV, desktop, your home office or in your conference room. They have become common decision aids for our personal and business meetings. Situational Applications provide rich information and data visualization aids for decision-orien...
SUBSCRIBE TO THE WORLD'S MOST POWERFUL NEWSLETTERS
SUBSCRIBE TO OUR RSS FEEDS & GET YOUR SYS-CON NEWS LIVE!
Click to Add our RSS Feeds to the Service of Your Choice:
Google Reader or Homepage Add to My Yahoo! Subscribe with Bloglines Subscribe in NewsGator Online
myFeedster Add to My AOL Subscribe in Rojo Add 'Hugg' to Newsburst from CNET News.com Kinja Digest View Additional SYS-CON Feeds
Publish Your Article! Please send it to editorial(at)sys-con.com!

Advertise on this site! Contact advertising(at)sys-con.com! 201 802-3021


SYS-CON FEATURED WHITEPAPERS

ADS BY GOOGLE