MemShrink progress, week 55-56

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.

On behalf of Facebook Inc, let me just say that we are prepared to give over one thousand free “pokes” to anyone who fixes this bug or alternatively https://bugzilla.mozilla.org/show_bug.cgi?id=683290. I will also gladly show you Mark Zuckerberg’s desk and give you free dinner and cookies as part of a complementary tour of Facebook HQ in Menlo Park.

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.

20 Responses to MemShrink progress, week 55-56

  1. #2: Better Foreground Tab Image Handling
    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.

    Suggestion link is wrong, twice link pasted.

    Also why not making experimental about:taskbar extension and using some code of about:noisy extension to report per tab memory reporting for easier to average user.

    To be really very honest with you, neglecting desktop Firefox and making dead investment in “Firefox OS” will be biggest mistake.
    Mozilla Corp, biggest Ace card is desktop Firefox and now Firefox on Android as well, but please neglect from doing overutilization of limited resources on dead investment.

    I appreciate your whole one year efforts in which you done marvellous job which in my opinion, no one other than you in mozilla can do.

    Also what about XML reporting.. There are few bugs in MemShrink list, on which heap classified goes pretty high. Fixing this will make your about:memory more comprehensive.

    • Nicholas Nethercote

      I fixed the broken link, thanks for that.

      How do you know Firefox OS is a dead investment? Would you have said the same about Firefox on Android six months ago?

      XML reporting is low-priority, it doesn’t come up much.

    • It can’t be a dead investment. I don’t think it will never become relevant as android or iOS but producing something this complicated in web technologies will push the firefox engine and they will be forced to improve the performance and memory usage. Something similar happened with pdf.js when the developer found bugs and other things implementing it. Or in windows 8, because optimizing for a low power tablet can improve things also for a powerful desktop.

  2. Have a great vacation – it is well deserved!

  3. You may keep an eye on bug 769273.

    It is about using the Kyle’s “nuke” feature but applied to addon sandboxes. It should save us from various leak reported against jetpack addons. We will be able to use it for the bootstrap.js sandbox too!

  4. How are frames reported? For instance, a page might have a bazillion Facebook buttons. Is it possible to report the memory in a tree? You might want to report each parent window twice, once as a container and once just for itself (which would be the first entry in the container).

  5. From Bug 704623 – track memory used by orphan DOM nodes in about:memory

    “Orphan” isn’t a verb.

    Orphan can be a verb. “They were orphaned when their parents died.” (That doesn’t necessarily make Orphan() a good function name, but it is a verb.)

  6. +1

  7. Hi Nicholas,

    It’s me again with another one of my rudimentary memory tests. I must state that compared with the vast majority of people that comment on this blog, I have no technical knowledge when it comes to Firefox.

    Anyway. I hit Ctrl+Shift+B and went into the library. I right click on folders and used the “Open All in Tabs” option to quickly open all of my 80+ bookmarks.

    I then commented on an article on http://www.ghacks.net and then made a drink. Came back a few minutes later and carried on reading for a bit. The memory usage was at about 830,000 kb. I then right clicked on my Gmail tab and selected the “Close Other Tabs” option.

    So with just Gmail open the memory does not go below 337,000 kb. It gets to that point pretty damn quickly, but then never goes below it.

    If I close Firefox, leave it for a few minutes and then restart it with just one tab displaying my Gmail inbox, memory usage hovers around 94,000 kb, which is much better, and what I would expect.

    I am using Firefox 14 beta 11 on a Dell laptop with 2.3 GHz dual core processor, 4GB RAM, Windows 7 Home Premium 64 bit, and the only add-ons that I have installed are “British English Dictionary 1.19.1″ and “Test Pilot 1.2.1″.

    More information about my RAM from Piriform’s Speccy:

    Memory
    Type DDR3
    Size 4096 MBytes
    Channels # Dual
    DRAM Frequency 399.0 MHz
    CAS# Latency (CL) 6 clocks
    RAS# to CAS# Delay (tRCD) 6 clocks
    RAS# Precharge (tRP) 6 clocks
    Cycle Time (tRAS) 15 clocks

    Not sure if that’s any use to you.

    I appreciate that Firefox has never been great at giving memory back once tabs have been closed but I would have thought that now we have reached version 14, things would have been better. I am not sure if this is a regression of some kind. I vaguely remember when I have carried out similar tests in the past, results have been a little better than this.

    If Nicholas or anyone else can look into this, that would be great.

    I am willing to email someone a small file will all my bookmarks in it if it will help, or I could post a list somewhere.

    • Nicholas Nethercote

      Which memory metric are you using, and what tool are you using to measure? What does about:memory say?

      As Andrew said, fragmentation (of various kinds) is the likely cause, and for this reason many programs have a quasi-memory of the memory consumption high-water mark. Firefox is one of those programs, and this is one area where it’s still not that good. One piece of good news is that generational GC should improve things quite a bit.

  8. When we’ve looked at this before, the cause of memory not going down again after closing all tabs was mostly fragmentation. Memory gets allocated into little chunks, and the chunks can’t be freed until every piece of the chunk gets freed. One of the goals, compacting GC, should help a lot of fragmentation caused by the JS heap. Justin Lebar has also done some investigation into reducing fragmentation caused by other allocations, but he hasn’t found any way to improve that yet.

  9. what a report, congrats for the progresses!

  10. This is really impressive progress from the position when memshrink started – well done! And have a good vacation.

  11. Nice work !

    May I suggest you to improve about:memory by expending the content to use the whole space in non-verbose mode ?

  12. Great work again Memteam!

    Snappy and Memshrink give great visibility to the work being done on the browser, do you know if there are plans for a blog for the team focusing on Crashes ? I noticed that there is a team called Crashkill, but I could not find a blog about it. I would be very interested in reading about their work, and I’m sure the community would enjoy seeing these three major parts of the browser being actively worked on.

    Have a nice vacation!

    • Nicholas Nethercote

      Crashkill has been around for quite some time, but as a project it is not highly visible. Robert Kaiser often discusses CrashKill stuff in his weekly update blog posts, but these are blandly titled “Weekly Status Report” so the content is not obvious. Also, I think he only writes about CrashKill stuff he has done, and not what others have done. Still, it might be worth looking at http://home.kairo.at/blog/. See also https://wiki.mozilla.org/CrashKill.

  13. I have noticed that Firefox 15 beta is causing an explosion of memory usage for me. The memory usage is much higher than Firefox 14 beta, which used to be around 500MB under the same circumstances.

    After a long period of web-browsing, I noticed that the memory usage was around 1.4 GB!! I closed all the other tab, ALL of them. Then opened an about:memory tab. Clicked “minimize memory usage” numerous times, but the usage did not go down. It seems every compartment is being leaked!

    ***********************************************
    Explicit Allocations
    1,271.16 MB (100.0%) — explicit
    ├────732.51 MB (57.63%) — js
    │ ├──621.62 MB (48.90%) ++ (669 tiny)
    │ ├───24.73 MB (01.95%) — compartment([System Principal], jar:file:///C:/Users/zzz/AppData/Roaming/Mozilla/Firefox/Profiles/gvrqe12c.default/extensions/%7Bd10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d%7D.xpi!/bootstrap.js)
    │ │ ├──18.39 MB (01.45%) ++ gc-heap
    │ │ └───6.33 MB (00.50%) ++ (7 tiny)
    │ ├───20.73 MB (01.63%) ── gc-heap-decommitted
    │ ├───20.03 MB (01.58%) ++ compartment([System Principal], about:blank)
    │ ├───18.09 MB (01.42%) — compartment([System Principal], chrome://browser/content/browser.xul)
    │ │ ├──13.89 MB (01.09%) ++ gc-heap
    │ │ └───4.21 MB (00.33%) ++ (5 tiny)
    │ ├───13.77 MB (01.08%) ++ compartment(http://www.youtube.com/watch?v=tgnrUz8lAiQ&feature=g-u-u)
    │ └───13.54 MB (01.06%) ++ compartment(atoms)
    ├────463.39 MB (36.45%) ── heap-unclassified
    ├─────35.97 MB (02.83%) — images
    │ ├──35.66 MB (02.81%) — content
    │ │ ├──35.66 MB (02.81%) — used
    │ │ │ ├──23.48 MB (01.85%) ── uncompressed-heap
    │ │ │ └──12.18 MB (00.96%) ++ (2 tiny)
    │ │ └───0.00 MB (00.00%) ++ unused
    │ └───0.31 MB (00.02%) ++ chrome
    ├─────25.21 MB (01.98%) — storage
    │ ├──23.74 MB (01.87%) ++ sqlite
    │ └───1.46 MB (00.12%) ── prefixset/all
    └─────14.08 MB (01.11%) ++ (10 tiny)

    Other Measurements
    0.21 MB ── canvas-2d-pixel-bytes
    1,271.10 MB ── explicit
    0.49 MB ── gfx-d2d-surfacecache
    4.21 MB ── gfx-d2d-surfacevram
    25.10 MB ── gfx-surface-image
    504 ── ghost-windows
    854.64 MB ── heap-allocated
    902.11 MB ── heap-committed
    47.45 MB ── heap-committed-unused
    5.55% ── heap-committed-unused-ratio
    3.20 MB ── heap-dirty
    126.35 MB ── heap-unused
    23.48 MB ── images-content-used-uncompressed
    389 ── js-compartments-system
    568 ── js-compartments-user
    414.00 MB ── js-gc-heap
    25.12 MB ── js-main-runtime-analysis-temporary
    260.28 MB ── js-main-runtime-gc-heap-allocated
    132.99 MB ── js-main-runtime-gc-heap-arena-unused
    0.00 MB ── js-main-runtime-gc-heap-chunk-clean-unused
    0.00 MB ── js-main-runtime-gc-heap-chunk-dirty-unused
    393.27 MB ── js-main-runtime-gc-heap-committed
    132.99 MB ── js-main-runtime-gc-heap-committed-unused
    51.09% ── js-main-runtime-gc-heap-committed-unused-ratio
    20.73 MB ── js-main-runtime-gc-heap-decommitted
    0.59 MB ── js-main-runtime-mjit
    126.54 MB ── js-main-runtime-objects
    214.30 MB ── js-main-runtime-scripts
    121.80 MB ── js-main-runtime-shapes
    31.18 MB ── js-main-runtime-strings
    30.07 MB ── js-main-runtime-type-inference
    0 ── low-commit-space-events
    0 ── low-memory-events-physical
    0 ── low-memory-events-virtual
    1,365.43 MB ── private
    1,275.38 MB ── resident
    0.00 MB ── shmem-allocated
    0.00 MB ── shmem-mapped
    23.74 MB ── storage-sqlite
    1,728.31 MB ── vsize
    0.55 MB ── window-objects-dom
    0.73 MB ── window-objects-layout-arenas
    0.04 MB ── window-objects-layout-pres-contexts
    0.46 MB ── window-objects-layout-style-sets
    0.00 MB ── window-objects-layout-text-runs
    0.60 MB ── window-objects-style-sheets