Memory Reporting

I changed things so that JavaScript memory consumption for web content is reported on a per-tab basis, as the following example shows.

│ ├────4,472,568 B (00.50%) -- top(http://www.mozilla.org/en-US/firefox/fx/, id=236)/active │ │ ├──4,192,640 B (00.47%) -- window(http://www.mozilla.org/en-US/firefox/fx/) │ │ │ ├──1,979,112 B (00.22%) ++ js/compartment(http://www.mozilla.org/en-US/firefox/fx/) │ │ │ ├──1,607,216 B (00.18%) ++ layout │ │ │ ├────352,520 B (00.04%) ── style-sheets │ │ │ ├────253,312 B (00.03%) ++ dom │ │ │ └────────480 B (00.00%) ── property-tables

All the major components of each tab’s memory consumption — JS, layout, style sheets, and DOM — are now reported on a per-tab basis. This is great, because it’s something that people have been requesting for years. However, the presentation is still intimidating. Consider the following excerpt from about:memory.

├───18,288,696 B (16.43%) -- window-objects │ ├───6,263,208 B (05.62%) ++ top(about:memory?verbose, id=6)/active │ ├───5,090,352 B (04.57%) ++ top(chrome://browser/content/browser.xul, id=1)/active │ ├───3,422,280 B (03.07%) ++ top(http://www.mozilla.org/en-US/firefox/fx/, id=17)/active/window(http://www.mozilla.org/en-US/firefox/fx/) │ ├───3,178,432 B (02.85%) ++ top(chrome://global/content/console.xul, id=21)/active │ └─────334,424 B (00.30%) ++ top(resource://gre-resources/hiddenWindow.html, id=3)/active/window(resource://gre-resources/hiddenWindow.html)

Among those top(...) entries are two browser tabs (one for www.mozilla.org, one for about:memory), two browser chrome windows (browser.xul is the main browser window, and console.xul is the error console window), and the hidden window which is always present. Firefox developers can easily understand this, and technically-inclined users may be able to guess what’s going on, but the average user will struggle.

From a user’s point of view, the visible things they have control over (i.e. windows and tabs) should be better distinguished from everything else, and be shown with nicer names than “browser.xul”. But the architecture of Firefox’s internals doesn’t make this easy. I’ve made one not-very-successful attempt to improve this, and I’d love to hear suggestions from other Firefox devs on how to present this information better.

(The step after that one is to present some kind of “tab manager” — a massively stripped-down variant of about:memory that ordinary users can understand. This would also require localization for all the languages that Firefox supports. One step at a time!)

Finally, another memory reporting improvement happened: Andrew McCreight and I added measurement of orphan DOM nodes. These are DOM nodes that have been discarded by a page but are still accessible from JavaScript objects. They were the biggest single remaining contributor to “heap-unclassified”. For example, when I hit the “update” button on about:memory I get 5MB of them (don’t worry, they go away when the garbage collector runs). Also, badly coded sites can create large numbers of them. So it’s a good thing to be able to measure, for both users and web developers.

Miscellaneous

Mihai Sucan fixed a bad leak involving pages that use window.console . Multiple people reported that this caused Firefox’s memory consumption to balloon to multiple GB. This leak was introduced during the Firefox 15 development cycle, and the fix has been backported to the Aurora channel, which is where Firefox 15 currently resides. One notable thing about this fix is that the problem was quite easy to diagnose because about:memory clearly indicated that the ConsoleAPI.js compartment was responsible for most of the memory consumption. Without the fine-grained memory reporting that was enabled by compartment-per-global, that memory would have been reported in a generic “System Principal” compartment which would have made diagnosis much harder.

Wladimir Palant fixed an issue in AdBlock Plus that causes its memory consumption to slowly grow when viewing sites that repeatedly reset the src property of images, such as Mibbit. (The fine-grained memory reporting enabled by compartment-per-global also made this diagnosis much easier.) This fix is in Adblock Plus 2.1.

Alexandre Poirot fixed a minor leak in the Add-on SDK.

I wrote about the difficulties of cross-browser memory comparisons.

Bug Counts

Here are the current bug counts.

P1: 20 (-3/+0)

P2: 90 (-3/+6)

P3: 102 (-6/+2)

Unprioritized: 2 (-0/+0)

A net reduction of seven bugs!

Update on The big ticket Items

In January, I described six “big ticket items” that needed tackling to improve Firefox’s memory consumption. Let’s look at how they’ve progressed since then.

#6: Better Script Handling

This had two parts. The first part was to only generate bytecode for a function once it is run. I’ve made a good chunk of progress towards and have it working on very simple examples. This has required a great deal of refactoring of the SpiderMonkey front-end. (The front-end is a hairy piece of code, so this is virtuous in and of itself.) There are still a couple of other bugs blocking it from further progress. I intend to return to continue on this once they are done.

The second part was to share immutable parts of scripts between web pages. No progress has been made on this.

So, overall, this item is about 25% done, and progress is still ticking along slowly.

#5: Better Memory Reporting

This one also had two parts. The first part was to reduce about:memory’s “heap-unclassified” number (a.k.a “dark matter”) to typically 10%. People such as Nathan Froyd and I have made good progress on this. It’s now occasionally below 10% (my current session shows 7.5%) but often is around 15%. That’s much better than the 20–25% we typically were getting at the start of the year, and it’s low enough that people rarely complain about it now. And we’re very much into the long tail now, so it’ll be hard to improve things much further. For the most part I don’t think we need to.

The second part was to report memory in a per-tab fashion. As mentioned above, I just completed this, although the presentation is not as user-friendly as it could be.

The per-tab reporting was enabled by compartment-per-global. Compartment-per-global also gives us much more insight into the memory consumption of Firefox’s own JavaScript code, and even some idea about add-on memory consumption, which was (to me) an unexpected bonus. Indeed, its usefulness was demonstrated by the two leak fixes mentioned above.

So, overall, this item is about 80% done. Although there is still some work to be done, it has progressed enough that I will remove it from the “big ticket items” list.

#4: Better Memory Consumption Tracking

We can cross this item off the list, thanks to areweslimyet.com. In January I also mentioned the idea of using telemetry for this, but we’ve found that telemetry results are so noisy that little (with rare exceptions) can be gleaned from it, so there’s not really anything to be done on that front.

#3: Compacting Generational GC

This is one of the two key SpiderMonkey performance features under development (the other being IonMonkey), and so people such as Terrence Cole, Steve Fink, Brian Hackett, and Bill McCloskey and actively working on it. Unfortunately, it’s a huge undertaking that requires rewriting many of SpiderMonkey’s internal and external APIs. If I had to guess I’d say it’s 25% complete, but that really is just a wild guess. I’ll be pleased if it is finished before the end of the year.

#2: Better Foreground Tab Image Handling

This is the big ticket item for which the least progress has been made. There’s a single bug that is blocking all the follow-on work. Both Timothy Nikkel and Jet Villegas have spent time working on it, but it has been a difficult nut to crack. Justin Lebar had a recent suggestion for narrowing its scope slightly. I hope it’s a good suggestion, because I’m out of ideas on this one.

#1: Better Detection and Notification of Leaky Add-ons

This is the most satisfying item of the lot. I expected to make only grudging, piecemeal progress, but Kyle Huey’s fix to prevent zombie compartments prevents the vast majority of add-on leaks, enough to declare victory and cross this item off the list. I’ll be writing more about this next week.

Summary

Three items (#1, #4, #5) have progressed enough that they can be removed from the list. Only one of these directly improved Firefox’s memory consumption; the other two were about better measuring and monitoring of memory consumption.

The remaining items are about reducing memory consumption. Two of them (#3, #6) have a long way to go, but progress has been made and is ongoing. One item (#2) has made little progress and has stalled.

The New big ticket Items

Here’s my updated list of the most important things.

#5: Better Script Handling

As mentioned above, this has two parts: lazy bytecode generation, and the sharing of immutable parts of scripts. I intend to continue working on the first part. Once that’s done, it may turn out that the second part isn’t necessary; we’ll see.

#4: Regain compartment-per-global losses

The measurements on areweslimyet.com have been creeping up for the past two months. The major cause is compartment-per-global, which has lots of benefits (as mentioned above) but also increases memory consumption by a small-to-moderate amount across the board. This is because there are many more compartments than there used to be, and there’s a certain amount of overhead and waste in each compartment.

Generational GC will help, but there are some other things that can be done more quickly that will help. See the tracking bug for one example.

#3: Boot2Gecko

Boot2Gecko (now officially known as “Firefox OS”) is going to be running on low-end phones without much physical memory, so that’ll require care. The first step is to get about:memory working on B2G. The next step is to find a way to copy the data from about:memory somewhere else, because B2G doesn’t have cut and paste! Beyond that, it’s unclear; but once about:memory is working it’s very likely it will identify some sub-optimal B2G-specific behaviour.

#2: Compacting Generational GC

Let me quote myself to explain the memory consumption benefits of compacting, generational GC.

Virtual memory consumption drops in two ways. First, the compaction minimizes waste due to fragmentation. Second, the heap grows more slowly. Physical memory consumption drops for the same two reasons.

The performance benefits are also significant.

Performance improves for three reasons. First, paging is reduced because of the generational behaviour: much of the JS engine activity occurs in the nursery, which is small; in other words, the memory activity is concentrated within a smaller part of the working set. Second, paging is further reduced because of the compaction: this reduces fragmentation within pages in the tenured heap, reducing the total working set size. Third, the tenured heap grows more slowly because of the generational behaviour… which means that structure traversals done by the garbage collector (during full-heap collections) and cycle collector are faster.

Important stuff.

#1: Better Foreground Tab Image Handling

On image-heavy pages, Firefox uses much more memory than other browsers. This includes things like Facebook image slideshows. One commenter said the following.

I have no idea if this person is really from Facebook, but it’s certainly suggestive. Also, I’ve heard from B2G people that they are having to work around this problem in their Gallery app.

To repeat myself from week 32: there are three MemShrink:P1 bugs relating to this: one about not decoding all images immediately, one about discarding non-visible decoded images after some time, and one about some infrastructure work that is required for the first two. (See this discussion on the dev-platform mailing list for more details about this topic.)

Summary

There are three repeat items, and two new items, but there are fewer items than last time; this reflects the fact that we are in a better position than we were in January. If you think I’ve overestimated or underestimated the importance of any of these, or omitted anything, I’d be interested to hear.

One final thing: last time, several people suggested “better reporting of add-on memory consumption”. If I knew of any ideas on how to do this better than about:memory currently does, I’d gladly put it on the list. But I don’t, and there doesn’t seem to be much point to put an item on the list that we don’t know how to implement.

Vacation

I’m taking the next two weeks as vacation, so there won’t be a MemShrink report on July 24. I’ll be back with the next MemShrink round-up on August 8th. See you then.