The newsrdr blog

Advertisement

The anatomy of a WebKit memory leak

Posted Sep 28, 2013 9:00:05 AM

Introduction

As you may know, newsrdr is a Web application that is designed to stay open in a browser tab in the background for potentially long periods of time. This means that it needs to manage memory usage carefully to ensure the best user experience. Much like most Web sites today, newsrdr uses JavaScript to handle UI and other front-end tasks, which has garbage collection.

I personally use newsrdr on both a 2010 MacBook Pro (running Safari) and on Chrome in Windows 7 to follow various tech blogs. Inevitably, Safari would always slow down on the Mac until I either restarted it or it reloaded all of my tabs. One time I had to do this and decided to check Activity Monitor first. I saw something like this:

"But I thought JavaScript had garbage collection," I said. Time to break out the tools and find out what was going on.

Memory profiling

Unfortunately, Safari does not have readily accessible memory profiling functionality. According to a post on Stack Overflow, instrumenting JavaScript involves running Instruments (an application that comes with Xcode). I started a new instance of Safari with just newsrdr open and played around a bit. Instruments was of little help, though, as it did not give me any sort of output. I could have played with it some more, but there was an easier alternative: Google Chrome.

See, Google Chrome has a full-featured JavaScript and DOM (Document Object Model) debugger. It also uses WebKit, so it's an almost perfect substitute for Safari. (I say "almost" because there was still a chance that I couldn't duplicate the issue due to minor differences in the code between the two browsers.) Anyway, choosing Tools->Developer Tools from the main Chrome menu brings up this awesome window:

Profiles sounds promising. Clicking on that brings up a tab with several options, including "Record Heap Allocations" and "Take Heap Snapshot". I took an initial heap snapshot, played around with the site a bit (basically: add a couple of feeds and go to Home), and took another snapshot.

Chrome has an option to compare the current snapshot with any previous snapshot, and here's what I discovered:

Of course, there should be no references to any <video> tags anywhere, since the site removes all post HTML from the DOM upon changing pages. Further investigation revealed that the feeds I had added contained embedded YouTube videos. Now I know why there are <video> tags, but why aren't they getting cleaned up?

The incredible persistent <iframe>

When someone embeds a YouTube video onto a Web page, the page contains an <iframe> tag that loads the video from YouTube's Web site. An <iframe> is a HTML tag that allows a developer to load another Web page inside the existing one. This allows the developer to do cool things. Like, say, loading embedded content based on the user's browser. When an <iframe> gets parsed by the browser, a new entry gets added to the window.frames array. This reference sticks around even after the <iframe> gets removed from the DOM due to it being part of the window and not the document. There's our reference to the video right there.

Fortunately, fixing this is simple. While we can't necessarily remove the <iframe> from there, we can remove the code and DOM that YouTube created by navigating to about:blank (which produces a blank HTML page). In CoffeeScript:

_leakCleanup: () ->
        # We need to navigate all iframes inside the article to about:blank first.
        # Failure to do so results in memory leaks.
        frameList = this.$('iframe')
        for i in frameList
            i.contentWindow.location.href = "about:blank"
        frameList.remove()

After adding this code and running the same test again, I found that the amount of heap used between the two snapshots was pretty much constant. Success!  I immediately checked this change into the tree.

Conclusion

In conclusion, memory leaks are fairly easy to find with the right tools. There's no excuse not to do at least a cursory check before releasing a new project or a change to an existing one. Hopefully this helped some people with their own Web projects.

Feel free to leave feedback below, or use the Contact link above if you run into other issues using newsrdr. 

comments powered by Disqus