Official website for Web Designer - defining the internet through beautiful design
FOLLOW US ON:
Author: Steve Jenkins
31st December 2012

Tune up your website with Chrome tools

Discover how to debug performance problems with Chrome’s JavaScript developer tools

Tune up your website with Chrome tools

JavaScript development has come a long way in recent years, and with that progress has come a new set of challenges and difficulties when it comes to application performance.
In the past, you might have added some jQuery effects to your page to hide or reveal text or buttons, loaded in some content with AJAX, or appended new elements to the DOM. But this was more or less always done in only a few places on the page, and only a few times for a given pageview. If you made a mistake that created dozens of objects in the memory and never cleaned them up, it usually wouldn’t matter since the user would soon navigate to another part of your site, and you’d get a clean slate to start again. Likewise, if you ever encountered the ‘a script on this page has become unresponsive’ error, it was almost always because of a simple coding error resulting in an infinite loop.
With the rapid growth of the single-page application style of site, however, as well as widespread adoption of so-called MV* JavaScript frameworks such as Backbone.js, Ember.js, and AngularJS, these assumptions no longer hold true. You might need to execute hundreds or thousands of operations to render out all the elements of a complex application, which might well run into the browser’s execution limit and cause it to halt your script. In addition, the user might interact with your page for several minutes or several hours without refreshing or navigating away from it , so it can be easy to accidentally consume an ever-increasing amount of memory.
These new challenges can be daunting, especially for new developers. Fortunately, there are some very good browser-based profiling tools for tracking down these problems, and some simple techniques we can use for resolving them.
Chances are you’re already familiar with the excellent set of developer tools available in Google’s Chrome browser. However, it’s more than likely that you’ve spent most of your time using the inspector in the Elements, Network, Console, and perhaps Resources tabs.
The performance debugging tools we’ll be looking at in this article live in the Timeline and Profiles tabs.

MEMORY TIMELINE

The Timeline tab allows you to map a chart of a page’s memory use over time. This is a great way to begin your investigation into potentially sluggish performance: if your app is leaking memory like a sieve, it will become quickly apparent.
To use the Timeline, open the Web Inspector (Tools>Developer Tools from the Chrome menu) and select the Timeline tab. Select Memory from the left-hand menu. At the bottom of the developer tools panel, there’s a solid circle icon (Record) which is used to begin recording the timeline. Click this, then interact with your page for a few seconds and you’ll see the timeline begin to appear. Under normal circumstances (assuming your app isn’t leaking memory), you’ll see the amount of memory used increase and decrease over time, generally returning to a baseline level. However, if there’s a problem, you might see memory use continue to increase indefinitely and exponentially.

Tune up your website with Chrome tools
Figure 1 shows an example timeline taken from a popular music streaming site, which is a good example of a big, complex, single-page JavaScript application. Notice that memory use increases over time, and then decreases suddenly every once in awhile. This is the result
of JavaScript’s garbage collector regularly freeing up any bits of memory used by objects that are no longer needed.
How does the garbage collector know that an object can be removed from memory? To be frank, the details are beyond the scope of this article, but in short, if an object can still be accessed by a reference to it, it won’t be garbage collected. For example, if you select a DOM node with jQuery and assign it to a variable, but later assign the value null to that variable, the jQuery object is essentially ‘orphaned’ and can be cleaned up (assuming you haven’t set other references pointing to it).

Tune up your website with Chrome tools
Figure 2, for example, shows what happens when the Timeline is run with a page designed to consume more memory each time a button is clicked.
This is exactly the kind of memory timeline you’d rather not see for your applications. But now that you know you’ve got memory leaks, how do you go about locating them so you can resolve them? Chrome’s web developer tools have a few more features that will help us narrow our search.

HEAP SNAPSHOTS

The next area of the web inspector we’ll be looking at is the Profiles tab. This actually consists of three separate tools grouped together because they behave similarly. Of the three, we’ll concern ourselves primarily with the first and last: CPU Profiles and Heap Snapshots.
Heap Snapshots are particularly useful for tracking down memory leaks, so let’s start there. When you select Take Heap Snapshot from the Profiles tab and click Start, Chrome will take a snapshot of all the objects and DOM nodes that currently reside in the memory for the current page.
Once the Snapshot is complete, you can select it from the left panel and explore the list of all the objects that were present at the time of the snapshot. Figure 3 shows one example of a Heap Snapshot.
Tune up your website with Chrome tools

Since most of the objects that JavaScript has in memory at any given time are native to the browser, and not related to your code, these snapshots can be very difficult to navigate. However, the real benefit comes when you compare two snapshots. In the figure above, you can see that the two snapshots are almost exactly the same size. This would correspond to a flat line in the memory Timeline we saw earlier. If, however, you took snapshots after the amount of memory used by the page had grown significantly, the snapshots would be noticeably different as well.
Try sorting the columns by size, and see if any objects or arrays appear near the top of the list in the later snapshot that were absent or much lower in the earlier snapshot. Investigating those, you’ll often find familiar object structures, as they will have been created by your code. This can frequently provide a great insight into which of the objects you’re creating are chewing up all that memory.
Chrome also provides some more sophisticated ways of comparing Heap Snaphsots. At the very bottom of the web inspector panel when you’re viewing a snapshot, there are two menus, shown in Figure 5. If, instead of All objects, you select Objects allocated between snapshots 1 and 2, you’ll be able to see new objects that might be contributing to the increase in memory usage.
Even more useful, if you select Comparison instead of Summary, you’ll be able to see how objects have changed between your selected snapshot and another one, as Figure 6 helpfully illustrates for us.
Then it’s merely a simple matter of ensuring that those objects are discarded when they’re no longer needed, usually by setting the variables that refer to them to null.

Tune up your website with Chrome tools

Tune up your website with Chrome tools

CPU PROFILES

CPU Profiles allow you to record your page’s JavaScript execution over its lifespan. It also provides a breakdown of where the browser has spent the majority of its time. This information can be invaluable when you need to track down code that is inefficient. For example, if you happen to discover that your page is spending 60 per cent of its JavaScript execution time inside a short function, you’ve got a good place to start your optimisation efforts.
Collecting a CPU Profile is much the same as taking a Heap Snapshot: in the Profiles tab, select Collect JavaScript CPU Profile, and click Start. You’ll see the profile recording in the left-hand panel. Once you’ve interacted with the problematic parts of your application, you can click Stop, then select the profile and inspect it.
The Self column gives you the amount of time spent inside each function, while the Total column adds in time spent in functions called by that function. You can expand each function to see a trace of where it was called from, and also click on the filename to see the function highlighted in the source code .
Figure 4 shows how a bit of digging can reveal where a problem lies. At first glance, most of the execution time seems to take place inside of jQuery, which isn’t much use to us. However, traversing back up through the trace, we find the place in our code where all these expensive jQuery calls are originating: line 3 of main.js.

USING TIMERS TO WORK AROUND EXECUTION LIMITS

Most often, you’ll be delving into these browser tools to try to ameliorate an application’s sluggish performance. But sometimes you’re dealing with a more serious issue: the browser locks up completely while trying to perform some complex operation, and alerts that a script on the page has become unresponsive.
Some browsers will alert this warning after a given number of script instructions have been executed, while others allow the script to run for a set number of seconds, but the core problem is the same: your script is trying to do too much in a single go. Sometimes you’ll be able to make your code more efficient, requiring fewer operations to reach the same result. However, this won’t always be possible.
The ideal solution to this problem is to use the Web Workers API introduced in HTML5, which allows you to delegate certain operations to background processes that won’t lock up execution of your script. However, web workers come with two significant limitations: they’re not supported in Internet Explorer (surprise surprise) below version 10 (which has yet to be released), or in the default Android browser (though they are supported in Chrome or Firefox for Android). What’s more, the background jobs have to be self-contained JavaScript files, and they run in a sandbox without any access to the rest of your scripts. That means no jQuery, and none of your Backbone models or views.
If you’re able to isolate some pure JavaScript heavy lifting that can easily be isolated from the rest of your code, and you’re happy with the level of browser support afforded by web workers, they’re a great solution. That said, a discussion of the Web Workers API is beyond the scope of this article. If you’re interested in learning more about the API, the Mozilla Developer Network has an excellent article covering all the basics at developer.mozilla.org/en-US/docs/DOM/Using_web_workers.
However, if those conditions don’t apply to you, there’s still a way out. Using JavaScript’s built-in timer functions, you can split out your one giant execution thread into a series of callback functions that will execute over a short span of time, giving the browser the illusion that your script has completed.
For example, let’s say you have a large array of Backbone views that you want to render, and rendering each of them requires a fair amount of processing: enough that your loop is causing the script to lock up and the browser to complain. Your code looks like this:

001 for (var i=0; i < myViews.length; i++) {
002 
003 var view = myViews[i];
004 
005 view.render();
006 
007 }
008 

Instead, you can move your renders into timers:
001 function renderViews(views) {
002 
003 setTimeout(function() {
004 
005 var view = views.shift();
006 
007 view.render();
008 if (views.length > 0) {
009 
010 setTimeout(arguments.callee, 20);
011 
012 }
013 
014 }, 20);
015 
016 }
017 
018 renderViews(myViews);

The timer renders the view, then sets up another timer to render the next view 20 milliseconds later. To the user, the views will appear to be drawn instantly, and the browser will happily run your script for as long as it takes, since each render takes place in a callback function, rather than taking place in the original operation.
You’ll need to adapt this idea a little for your code, but the idea should be clear: rather than performing your computationally intensive processes directly inside the loop, spin them out into timer callbacks.

GET UP TO SPEED

Debugging and resolving JavaScript performance problems can be tricky – certainly much trickier than debugging coding errors. But with the help of the strong diagnostic tools that are present in modern browsers, and in Google Chrome specifically, it becomes a little bit easier to locate the source of a problem and begin to think about how to resolve it. Combine this with a little bit of know-how about improving performance, and your application should be speeding along in no time.

  • Tell a Friend
  • Follow our Twitter to find out about all the latest web development, news, reviews, previews, interviews, features and a whole more.