Categories
about:memory Firefox Memory consumption MemShrink

MemShrink progress, week 8

A hodge-podge of things happened this week.

The MemShrink bug counts changed as follows.

  • P1: 29 (-2, +4)
  • P2: 62 (-5, +14)
  • P3: 41 (-0, +6)
  • Unprioritized: 2 (-10, +1)

We actually got through almost all the unprioritized bugs in today’s MemShrink meeting, which was good, but the counts are still going up.  Fortunately, most of the new bugs are ideas for improvement that are reported by developers.  My gut feeling (which I get from reading a lot of memory-related bug reports) is that the number of reports from users about high memory usage are much lower than they were a few months ago.

Categories
about:memory Firefox Memory allocation Memory consumption

Clownshoes available in sizes 2^10+1 and up!

I’ve been working a lot on about:memory lately.  It’s a really useful tool, but one frustrating thing about it is that a decent chunk of our memory usage is not covered by any of the existing memory reporters, and so is falling into the “heap-unclassified” bucket:

This bucket typically accounts for 30–45% of about:memory’s “explicit” measurement.  We’ve discussed this “dark matter” several times in MemShrink meetings.  There’s lots of work underway to add new reporters to uninstrumented parts of Gecko, but I’ve seen cases where it looks like a decent chunk of the missing memory is due to the JS engine, which is already seemingly thoroughly instrumented.

This week I realized that some of the dark matter could be due to “slop” from jemalloc, the browser’s heap allocator.  What is slop?  When you ask a heap allocator for a certain number of bytes, it’ll often give you back more than you asked for, rounding the size up.  This wastes some memory, but there are good reasons for it — it makes the heap allocator much faster and simpler, and helps avoid fragmentation when the memory is freed.

The following comment from jemalloc.c shows jemalloc’s size classes.  Any request that’s not for one of the listed sizes below is rounded up to the nearest size.

*   |=====================================|
*   | Category | Subcategory    |    Size |
*   |=====================================|
*   | Small    | Tiny           |       2 |
*   |          |                |       4 |
*   |          |                |       8 |
*   |          |----------------+---------|
*   |          | Quantum-spaced |      16 |
*   |          |                |      32 |
*   |          |                |      48 |
*   |          |                |     ... |
*   |          |                |     480 |
*   |          |                |     496 |
*   |          |                |     512 |
*   |          |----------------+---------|
*   |          | Sub-page       |    1 kB |
*   |          |                |    2 kB |
*   |=====================================|
*   | Large                     |    4 kB |
*   |                           |    8 kB |
*   |                           |   12 kB |
*   |                           |     ... |
*   |                           | 1012 kB |
*   |                           | 1016 kB |
*   |                           | 1020 kB |
*   |=====================================|
*   | Huge                      |    1 MB |
*   |                           |    2 MB |
*   |                           |    3 MB |
*   |                           |     ... |
*   |=====================================|

In extreme cases, jemalloc will return almost double what you asked for.  For example, if you ask for 1,025 bytes, it’ll give you 2,048.  A lot of the time you have to just live with slop;  if you need to heap-allocate an object that’s 680 bytes, jemalloc will give you 1,024 bytes, and you just have to accept the 344 bytes of waste.  But if you have some flexibility in your request size, it’s a good idea to pick a size that’s a power-of-two, because that always gives you zero slop.  (Remember this, it’s important later on.)

So I instrumented jemalloc to print out an entry for every heap allocation, showing the requested amount, the actual allocated amount, and the resulting slop.  I then started up Firefox, opened Gmail, then shut down Firefox.  Next, I ran the resulting log through a wonderful little concordance-like script I have called “counts” which analyzes a file and tells you how many times each distinct line occurs.  Here’s the top ten lines of the output:

909062 numbers:
( 1 ) 205920 (22.7%, 22.7%): small:     24 ->     32 (     8 )
( 2 )  66162 ( 7.3%, 29.9%): small:     72 ->     80 (     8 )
( 3 )  61772 ( 6.8%, 36.7%): small:     40 ->     48 (     8 )
( 4 )  54386 ( 6.0%, 42.7%): small:   1056 ->   2048 (   992 )
( 5 )  48501 ( 5.3%, 48.0%): small:     18 ->     32 (    14 )
( 6 )  47668 ( 5.2%, 53.3%): small:     15 ->     16 (     1 )
( 7 )  24938 ( 2.7%, 56.0%): large:   4095 ->   4096 (     1 )
( 8 )  24278 ( 2.7%, 58.7%): small:     56 ->     64 (     8 )
( 9 )  13064 ( 1.4%, 60.1%): small:    104 ->    112 (     8 )
(10 )  12852 ( 1.4%, 61.6%): small:    136 ->    144 (     8 )

There were 909,062 lines, which means there were 909,062 allocations.  (I didn’t print information about frees.)  The most common line was “small: 24 -> 32 ( 8 )” which occurred 205,920 times, accounting for 22.7% of all the lines in the file.  The “small” refers to jemalloc’s class size categories (see above), and every allocation request of 24 bytes was rounded up to 32 bytes, resulting in 8 bytes of slop.

Looking through the list, I saw a case where 1,048,578 (2^20+2) bytes had been requested, and jemalloc had returned 2,097,152 (2^21) bytes.  That’s a huge (1MB) waste, and also smelled very fishy.  And on further inspection there were a lot of smaller but still suspicious cases where a number slightly larger than a power-of-two was being rounded up to the next power-of-two:  1,032 to 2048;  1,056 to 2,048;  2,087 to 4,096;  4,135 to 8,192, etc.

I investigated a number of these by adding some code to jemalloc to detect these suspicious request sizes, and then using Valgrind to print out a stack trace every time one occurred.  What I found was four distinct places in the codebase where the code in question had flexibility in the amount of memory it allocated, and so had tried to ask for a power-of-two, but had botched the request and thus ended up asking for slightly more than a power-of-two!

  • In js/src/vm/String.cpp, when concatenating two strings, the capacity of the resultant string is rounded up to the next power-of-two (unless the string is really big, in which case the rounding is less aggressive).  This is a reasonable thing to do, as it means that if a further concatenation occurs, there’s a good chance there’ll be space for the extra chars to be added in-line, avoiding an extra allocation and copy.  But the code first did its power-of-two round-up, and then added sizeof(jschar) (which is two bytes) to the size, to allow space for the terminating NULL char.  This code was responsible for the 1,048,578 byte request I mentioned earlier.  Luke Wagner fixed this bug with a minimal change earlier this week.  I was unable to easily measure the overall cost of this, but avoiding 1MB of slop with some frequency can only be a good thing.
  • nsprpub/lib/ds/plarena.c implements an arena allocator.  Each arena pool is given a size for each arena when it’s created, and the size passed in is almost always a power-of-two such as 1,024 or 4,096.  Which is good, except that the allocator then has to add a few more bytes onto the request because each arena has a header that holds book-keeping information.  When I start Firefox and load Gmail on my Linux64 box, I see that approximately 3MB of space is wasted because of this bug.  The fix is simple, it just requires that the arena payload be reduced by the size of the header;  my patch is awaiting review.  Sadly enough, this problem was identified 3.5 years ago but not fixed.
  • js/src/jsarena.cpp is almost identical to nsprpub/lib/ds/plarena.c and the story is the same:  it has the same problem; it was first identified 3.5 years ago but not fixed; and my patch is awaiting review.  This didn’t make much difference in practice because this problem had been separately identified for the particular JSArenaPool that allocates the most memory, and worked around using an ill-documented hack.
  • db/sqlite3/src/sqlite3.c is the best one of all.  SQLite is very careful about measuring its memory usage and provides an API to access those measurements.  But in order to measure its own memory usage accurately, it adds 8 bytes to every allocation request in order to store the requested size at the start of the resulting heap block.  In doing so, it converts a lot of 1,024 byte requests into 1,032 byte requests.  Ironically enough, the slop caused by storing the requested size rendered the stored size inaccurate.  I determined that as a result, SQLite is using roughly 1.15–1.20x more memory in Firefox than it thinks it is.  So, go look at the “storage/sqlite” number in about:memory and mentally adjust it accordingly.   (On my Mac laptop which has an old profile and 11 tabs open it’s currently 70MB, so there’s probably an additional 10–14MB of slop).  SQLite is an upstream product, and the authors are working on a fix.

Once all these fixes have landed, there will be two benefits.  First, Firefox’s memory usage will go down, which is good for everyone.  Second, the proportion of dark matter in about:memory will also drop, which makes our memory profiling more accurate and useful.

After that, converting existing reporters to use malloc_usable_size will account for much of the legitimate slop, further reducing the amount of dark matter. And then I have a master plan for identifying whatever’s left.

clownshoes

So what’s up with this post’s title?  Andreas Gal uses the term “clownshoes” to refer to code that is laughably bad.  When I found and filed the first of these bugs, in a fit of whimsy I put “[clownshoes]” in its whiteboard.  Only later did a realize how suitable this was: a clown’s shoes and these botched power-of-two-sized allocations both have lots of empty space in them, and thus make their owner less nimble than they could be.  Perfect.

Categories
about:memory Firefox Memory consumption MemShrink

MemShrink progress, week 7

Lots of good stuff this week.

Blog posts

There were four great blog posts this week relating to memory usage in Firefox.

Web workers

Ben Turner landed a complete reworking of web workers.  This fixed three MemShrink bugs, and probably a number of others.  Ben also added memory reporters so that web workers show up in about:memory.

Add-ons

I always cringe when someone files a bug report complaining about Firefox’s memory usage and they have many (e.g. 10, 20 or more) add-ons installed.  The chances of all of those add-ons behaving themselves is pretty low, unfortunately.  For example, one user found that the CyberSearch 2.0.8 add-on causes the places SQLite database to grow to over 300MB.  Another user found that one or more add-ons caused that same database to grow to 1.2GB(!);  the add-on(s) responsible has not yet been identified.

This kind of thing is terrible for perceptions of Firefox.  Maybe limiting the size of places.sqlite will help?

In better add-on-related news, Jan Honza Odvarko fixed a bad memory leak in Firebug, one that was causing zombie compartments on pages where Firebug hadn’t even been enabled.  Now Firebug only causes zombie compartments for pages where Firebug has been enabled, but Steve Fink is making progress there, as he described in his blog post that I linked to above.

Two other add-ons known to cause memory problems are Lastpass and It’s All Text.

Miscellaneous

Andrew McCreight is making great progress on his static analysis to find cycle collection leaks.  This week he found a problem in nsDOMEventTargetWrapperCache, which Olli Pettay then fixed.

If you ask a heap allocator like jemalloc for a certain number of bytes, it’ll often round that request size up to a number such as a power-of-two.  For example, if you ask for 99 bytes it’ll give you 112 or 128.  I found that this is contributing to the high “heap-unclassified” number in about:memory.  I also stumbled across two cases where Firefox itself deliberately requests a power-of-two-sized block of memory (with the aim of avoiding round-up in the heap allocator) but botches the calculation such that it asks for a block slightly bigger than a power-of-two, which jemalloc then rounds up again.  Luke Wagner fixed the first of these, and I have patches awaiting review to fix the second.

Finally, I should mention bug 658738 and its children.  Dão Gottwald and others have been working tirelessly to fix leaked windows in browser-chrome tests.  This has involved both (a) fixing problems with the tests, and (b) fixing problems with the browser.  Eleven child bugs have already been fixed, and there are four still open.

Bug counts

Here’s the change in MemShrink bug counts.

  • P1: 27 (+1)
  • P2: 53 (+4)
  • P3: 35 (+2)
  • P4: 11 (+5)

Increases all across the board.  Once again I think this reflects the increasing amount of work being done… but I keep saying that.  Therefore, beginning next week, I’ll show how many bugs in each category were fixed and how many were added, e.g. “P1: 27 (-2, +3)”.  That’ll give a better idea of progress.  (I can’t start it until next week because I’ve only today started recording enough info about the open bugs to get those numbers.  And before you suggest that I use a time-based Bugzilla search instead, the problem with that is that I write this progress report at a slightly different time each week, so if I just search during the last week I may double-count or fail to count some bugs.)

Categories
about:memory Firefox Memory consumption MemShrink

MemShrink progress, week 6

Dave Hunt wrote this week about some endurance tests comparing Firefox 4, 5, 6, 7, and 8, which show how Firefox’s memory usage is improving.  Pay most attention to the ‘resident’ numbers for Firefox 6, 7 and 8, which indicate how much physical memory is being used.  The ‘explicit’ numbers show a big drop between 6 and 7, and then a rise between 7 and 8, but they are suspect, so don’t panic yet.

about:memory improved again this week.  I added a reporter for each compartment’s property table, and two reporters js-compartments-system and js-compartments-user which count how many compartments are present. These latter two will be added to telemetry soon.

I also added a reporter that computes the fraction of the JS heap that is unused, called js-gc-heap-unused-fraction.  The results are surprising — numbers like 30% and 50% are common.  (Some preliminary JS heap visualization helps explain why.)  There are some suggestions for short-term improvements to mitigate this (e.g. smaller GC chunks).  However, I suspect that to fix it properly will require a compacting garbage collector — the current mark and sweep algorithm unavoidably leaves lots of holes all over the heap each time it runs.  But I could be wrong!  GCs have a large design space and there may be other solutions.  A non-compacting generational GC would help in its own way too — fewer objects would reach the general heap and so each full heap collection would likely collect less garbage, and thus leave fewer holes.

Here’s this week’s bug count:

  • P1: 30 (+4)
  • P2: 48 (-1)
  • P3: 33 (+0)
  • Unprioritized: 8 (+2)

It’s nice to see the P2 count go down!  Note that bugs remain unprioritized for two reasons.  First, we don’t always get through all the unprioritized bugs in the MemShrink meeting.  Second, sometimes we ask for more investigation or data before assigning a priority.

On the topic of bugs, I closed the meta-bugs for tracking leaks against Firefox releases, and the one for tracking non-leak memory use reductions, as I mooted last week.  I kept open the one for improving memory-related tools because multiple people though it was useful.

Categories
about:memory compartments Firefox Memory consumption MemShrink

MemShrink progress, week 5

There were four main areas of MemShrink progress this week.

Killing zombie compartments

A zombie compartment is one that outlives its tab.  Kyle Huey fixed a very common class of short-lived zombie compartments caused by XmlHttpRequests.  These zombie compartments only lived for a couple of minutes, but this is an important fix because it makes makes memory usage follow tab usage better, and also makes detecting longer-lived zombie compartments much easier.

Alon Zakai has also been doing heroic work tracking down a zombie compartment related to web workers exhibited at coffeekup.org.  I’m sure he’d appreciate additional help from anyone who might be able to provide it.

Improving about:memory

Lots of progress here.

  • I changed the per-compartment memory reporters so that multiple system compartments with the same name are reported separately, instead of being merged.  This made it obvious that JetPack-based add-ons can have dozens of system compartments.  I don’t know if anybody realized this previously, and it’s something of a concern.  The compartments are currently distinguished in about:memory only by their address, as the following example shows.  system principal including its address
    It would be great to add some identifying information that indicates what part of the code is responsible for creating each system compartment.  That might even give us some much-needed per-add-on accounting.
  • Andrew McCreight added a memory reporter for the cycle collector.
  • I added a memory reporter for the Necko (network) memory cache.
  • Justin Lebar fixed a problem with the image memory reporters, but it bounced due to a possible Tp5 RSS/PrivateBytes regression on Mac.  This was surprising, maybe it was just noise?
  • I changed about:memory so that if there are multiple memory reporters with the same name, you can tell they were merged into a single entry.  For example, in the following screenshot you can easily tell that there were four separate connections to the places database.
    places entry indicating duplication

Acting on memory pressure

Justin Lebar made some great progress on triggering memory pressure events when physical or virtual memory is low on Windows.  See comments 28 and 29 on that bug for some nice graphs showing how this kept Firefox responsive when browsing some very image-intensive sites.  This kind of adaptive behaviour is really important for getting Firefox to behave well on the full range of supported devices, from smartphones all the way up to desktop machines with lots of RAM.  Go Justin!

Tweaking MemShrink processes

The MemShrink wiki page used to be vague about the project’s goals.  So this week I made it more precise.

[The] goal is to get the number of MemShrink P1 bugs down to zero. That will mean that all the bad leaks will have been fixed, and also the important auxiliary work (e.g. infrastructure to detect regressions) will be in place.

As a result, Jeff Muizelaar changed areweslimyet.com to point to the list of open MemShrink P1 bugs.  It previously pointed to a Tp5 Talos graph.

On a related noted, we have six open meta-bugs for tracking leaks against Firefox releases, one for tracking non-leak memory use reductions, and one for improving memory-related tools.  (These are listed on the wiki.)  I created these bugs before MemShrink meetings started.  But now that we are using the MemShrink whiteboard annotations assiduously, these tracking bugs don’t seem necessary — having two tracking mechanisms is overkill.  In particular, I think their dependencies aren’t being updated consistently.  So I propose to stop using them and close them.  If you have any objections, please let me know and I’ll reconsider.  If I do close them, I’ll make sure that all bugs blocking them have a MemShrink annotation so they won’t fall through the cracks.

And that segues nicely into the MemShrink bug count for this week:

  • P1: 26 (+2)
  • P2: 49 (+0)
  • P3: 33 (+4)
  • Unprioritized: 6 (+4)

Like last week, this increase mostly reflects the fact that people are coming up with new ideas for improvements.

Finally, thanks to Jesse Ruderman for taking minutes at this week’s meeting (and the previous two).

Categories
about:memory JägerMonkey Memory consumption Tracemonkey

You make what you measure

Inspired by Shaver’s patch, I implemented some per-compartment stats in about:memory.  I then visited TechCrunch because I know it stresses the JS engine.  Wow, there are over 70 compartments!  There are 20 stories on the front page.  Every story has a Facebook “like” button, a Facebook “send” button, and a Google “+1” button, and every button gets its own compartment.

That sounds like a bug, but it’s probably not.  Nonetheless, every one of those buttons had an entry in about:memory that looked like this:

Old compartment measurements

(The ‘object-slots’ and ‘scripts’ and ‘string-chars’ measurements are also new, courtesy of bug 571249.)

Ugh, 255,099 bytes for a compartment that has only 971 bytes (i.e. not much) of scripts?  Even worse, this is actually an underestimate because there is another 68KB of tjit-data memory that isn’t being measured for each compartment.  That gives a total of about 323KB per compartment.  And it turns out that no JIT compilation is happening for these compartments, so all that tjit-data and mjit-code space is allocated but unused.

Fortunately, it’s not hard to avoid this wasted space, by lazily initializing each compartment’s TraceMonitor and JaegerCompartment.  With that done, the entry in about:memory looks like this:

New compartment measurements

That’s an easy memory saving of over 20MB for a single webpage.  The per-compartment memory reporters haven’t landed yet, and may not even land in their current form, but they’ve already demonstrated their worth.  You make what you measure.

Categories
about:memory Correctness Firefox SQLite

Asynchronous database connections must be closed with asyncClose()

TL;DR:  if you’re familiar with any Mozilla (C++ or JS) code that opens an async database connection, please go and check that it calls asyncClose() to close the connection;  not doing so can lead to crashes and/or memory leaks.

Specifically, in Firefox 6, when such a connection is destroyed (because it’s explicitly deallocated or its refcount drops to zero in C++, or it’s garbage collected in JS) it’ll currently cause sporadic crashes in about:memory or telemetry code or Test Pilot code.  This is because memory reporters end up pointing to connections that have been freed, and so when they are used they end up (innocently) accessing memory they shouldn’t.

I’ve opened bug 662989 to avoid the crashes, but even once it’s implemented, I think that if you fail to call asyncClose() it will still cause a memory leak, because sqlite3_close() is never called on the connection and so SQLite won’t free up memory used by the connection.  Also, connections that needlessly remain open can have active locks that can starve out other connections or cause extreme growth of the write-ahead-log.

As I write this, thanks to Marco Bonardo, I’m aware of three places in JS code that fail to call asyncClose() when they should:

  • browser/components/preferences/aboutPermissions.js.  Bug 654573 covers this, I’m about to land a patch to fix it.
  • toolkit/components/contentprefs/nsContentPrefService.js.  Bug 662986 covers this.
  • browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/experiment_data_store.js.  Bug 662985 covers this.  Note that Test Pilot seems to fail to use asyncClose() when it should, and it also uses memory reporters.  So it’s unclear which of these two things is responsible for the Test Pilot crashes of this sort seen so far;  in other words, Test Pilot may be a culprit or an innocent bystander, or both.

These were found via a crude MXR search.  I haven’t looked for C++ code that makes this mistake.

If you only do synchronous transactions over your database connection, the connection will be cleaned up properly, even if you don’t call close(), when it is deallocated or garbage collected.  However, it’s better to close the connection as soon as you can so that the resources (memory, locks) are freed immediately.

See this MDC page for more details about database connections and the relevant APIs.  It appears that these APIs are somewhat error-prone.  As it happens, an NS_ENSURE_TRUE macro will fail when an async connection is destroyed without having been asyncClose’d first, and this causes a warning message to be printed to stderr.  Unfortunately, stderr is spammed with all sorts of warnings (something that was discussed in the comments of a previous post of mine), and so this warning gets lost among the noise.  Addressing this problem is something I’ll write about shortly.

Thanks to Andrew Sutherland and Marco Bonardo for helping me greatly with the bugs mentioned above.  Any errors in this post are mine!

UPDATE: I did some investigation and found that about:permissions leaked about 180KB of memory on my machine every time it failed to asyncClose a DB connection.   This showed up under the explicit/storage/sqlite/other reporter in about:memory.

Categories
about:memory Firefox Memory consumption MemShrink

Leak reports triage, May 24, 2011

I’ve been tracking recent memory leak reports in bug 640452. I’m doing this because I think memory leaks hurt Firefox, in terms of losing users to other browsers, as much as any other single cause. (I suspect pathological performance slow-downs due to old and busted profiles hurt almost as much, but that’s a topic for another day.)

There are 61 bugs tracked by bug 640452, 21 of which have been resolved.  Any and all help with the 40 remaining would be most welcome. For each bug I’ve put in square brackets a summary of the action I think it needs.

  • [NEEDS ANALYSIS]:  needs someone to attempt to reproduce, try to work out if the problem is real and still occurring.
  • [NEEDS WORK]: problem is known to be real, needs someone to actually fix it.
  • [PENDING EVANGELISM]: problem with a website is causing a leak, needs someone to check the site has been fixed.
  • [CLOSE?]: bug report is unlikely to go anywhere useful.  Closing it (with a gentle explanation) is probably the best thing to do.
  • [GGC]: needs generation GC to be fixed properly.

Here are the bugs.

  • 497808: This is a leak in gmail, caused by a bug in gmail — when an email editing widget is dismissed, some stuff isn’t unlinked from a global object that should be.  Google Chrome also leaks, but a smaller amount, it’s unclear why. The bug is assigned to Peterv and is still open pending confirmation that it’s been fixed in gmail. [PENDING EVANGELISM]
  • 573688: Valgrind detects several basic leaks in SQLite.  Assigned to Sayre, no progress yet. [NEEDS WORK]
  • 616850: Huge heaps encountered when browsing www.pixiv.net, leading to incredibly slow cycle collections (3 minutes or more!)  Little progress. [NEEDS ANALYSIS]
  • 617569: Large heaps encountered for some pages using web workers.  Looks like it’s not an actual leak.  Assigned to Gal, he says a generational GC would help enormously, so probably nothing will happen with this bug until that is implemented (which is planned).  I marked it as depending on bug 619558. [GGC]
  • 624186: Using arguments.callee.caller from a JSM can trigger an xpcom leak.  The bug has a nice small test that demonstrates the problem.  Unassigned. [NEEDS WORK]
  • 631536: A bad string leak, seemingly on Windows only, with lots of discussion and a small test case.  Assigned to Honza Bambas.  Was a Firefox 4.0 blocker that was changed to a softblocker at the last minute without any explanation.  Seems close to being fixed. [NEEDS WORK]
  • 632012: Firefox 4 with browser.sessionstore.max_concurrent_tabs=0 uses a lot more memory restoring a session with 100s of tabs than Firefox 3.6 with BarTab.  Unassigned.  Unclear if this is a valid comparison.  [CLOSE?]
  • 634156: Identifies some places where the code could avoid creating sandboxes.  Assigned to Mrbkap, he said (only four days ago) he has a patch in progress.  Seems like it’s not actually a leak, so I changed it to block bug 640457 (mslim-fx5+). [NEEDS WORK]
  • 634449: A classic not-very-useful leak report.  One user reported high and increasing memory usage with vague steps to reproduce.  Two other users piled on with more vague complaints.  The original reporter didn’t respond to requests for more measurements with a later version.  I’m really tempted to close bugs like this, they’ll never lead anywhere.  Unassigned. [CLOSE?]
  • 634895: Vague report of memory usage increasing after awakening a machine after hibernation, with one “me too” report.  Unassigned.  Unlikely to lead to any useful changes.  [CLOSE?]
  • 635121: A leak in Facebook Chat, apparently it’s Facebook’s fault and occurs in other browsers too.  (Unfortunately, leaks like that hurt us disproportionately because we don’t have process separation.)  Assigned to Rob Arnold, marked as a Tech Evangelism bug.  Unclear if the Facebook code has been fixed, or if Facebook has even been contacted. [PENDING EVANGELISM]
  • 635620: Very vague report.  Unlikely to go anywhere.  Unassigned. [CLOSE?]
  • 636077: Report of increasing memory usage, with good test case.  Lots of discussion, but unclear outcomes.  Again, generational GC could help.  MozMill endurance tests showed the memory increase flattening out eventually.  Might be worth re-measuring now.  Assigned to Gal.  I marked it as depending on the generational GC bug (bug 619558). [NEEDS ANALYSIS, GGC]
  • 636220: Memory usage remains high after closing Google Docs tabs.  Assigned to Gal.  Needs more attempts to reproduce. [NEEDS ANALYSIS]
  • 637449: Looks like a clear WebGL leak.  Might be a duplicate of, or related to, bug 651695. Unassigned, but Bjacob looked into it a bit. [NEEDS ANALYSIS]
  • 637782: Memory usage increases on image-heavy sites like http://www.pixdaus.com/ or http://boston.com/bigpicture/ or http://www.theatlantic.com/infocus/.  Lots of discussion but not much progress.  Unclear if the memory is being released eventually.  Needs more analysis.  Unassigned.  [NEEDS ANALYSIS]
  • 638238: Report of memory increasing greatly while Firefox is minimized.  Might be related to RSS Ticker?  I would recommend giving up on this one except the reporter is extremely helpful (he’s participated in multiple bugs and I’ve chatted to him on IRC) and so progress might still be made with some effort.  Unassigned. [NEEDS ANALYSIS]
  • 639186: AdBlock Plus and NoScript together causing a leak on a specific page.  Lots of discussion but it petered out.  Unassigned.  [NEEDS ANALYSIS]
  • 639515: GreaseMonkey causes a big memory spike when entering private browsing.  Some discussion that went nowhere.  Unassigned.  [NEEDS ANALYSIS]
  • 639780: Report of steadily increasing memory usage leading to OOMs.  Steps to reproduce are vague, but the reporter is very helpful and collected lots of data.  Unassigned.
  • 640923: Vague reports of increasing memory usage, lots of people have piled on.  One useful lead:  RSS feeds might be causing problems on Windows 7?  The user named SineSwiper (who has alternated between being abusive and collecting useful data) thinks so.  Unassigned. [NEEDS ANALYSIS]
  • 642472: High memory usage on a mapping site.  Very detailed steps to reproduce;  one other user couldn’t reproduce.  Unassigned.  [NEEDS ANALYSIS]
  • 643177: Vague report.  Unassigned.  [CLOSE?]
  • 643940: Ehsan found leaks in the HTML5 parser with the OS X ‘leaks’ tool.  Unassigned.  [NEEDS ANALYSIS]
  • 644073: Ehsan found a shader leak with the OS X ‘leaks’ tool.  Unassigned. [NEEDS WORK]
  • 644457: High memory usage with gawker websites and add-ons (maybe NoScript?)  See comment 25. Unassigned.  [CLOSE?]
  • 644876: Leak with AdBlock Plus and PageSpeed add-ons on mapcrunch.com.  Unassigned.  [NEEDS ANALYSIS]
  • 645633: High memory usage with somewhat detailed steps to reproduce.  Reporter is helpful and has collected various pieces of data.  [NEEDS ANALYSIS]
  • 646575: Creating sandboxes causes leaks.  Good test case.  Unassigned.  [NEEDS WORK]
  • 650350: Problem with image element being held onto when image data has been released.  Bz said he would look at it.  Unassigned. [NEEDS ANALYSIS]
  • 650649: with only about:blank loaded, memory usage ticks up slightly.  Some discussion;  it may be due to the Urlclassifier downloading things.  If that’s true, it makes diagnosing leaks difficult. [NEEDS ANALYSIS]
  • 651695: Huge WebGL leak in the CubicVR demo.  Unassigned.  [NEEDS WORK]
  • 653817: Memory increase after opening and closing tabs.  A lot of discussion has happened, it’s unclear if the memory usage is due to legitimate things or if it’s an actual leak.  Assigned to me.  [NEEDS ANALYSIS]
  • 653970: High memory usage on an image-heavy site.  Comment 5 has a JS snippet that supposedly causes OOM crashes very quickly.  Unassigned.  [NEEDS ANALYSIS]
  • 654028: High memory usage on Slashdot.  Seems to be because Slashdot runs heaps of JavaScript when you type a comment.  Lots of discussion, seems to be due to bad GC heuristics and/or lack of generational GC?  Unclear if there’s an actual leak, or just delayed GC.  Unassigned.  [NEEDS ANALYSIS]
  • 654820: Leak in JaegerMonkey’s regular expression code generator caught by assertions.  Assigned to cdleary.  [NEEDS WORK]
  • 655227: Timers using small intervals (100ms or less) are never garbage collected(!)  Unassigned.  [NEEDS ANALYSIS]
  • 656120: Bug to do GC periodically when the browser is idle.  Assigned to Gwagner, has a patch.  [NEEDS WORK]
  • 657658: test_prompt.html leaks.  Unassigned.  [NEEDS WORK]

You can see that most bugs are marked as “[NEEDS ANALYSIS]”.  The size of the problem and the amount of developer attention it is receiving are not in proportion.

One thing I want to do is write a wiki page explaining how to submit a useful leak report, in an attempt to avoid the vague reports that never go anywhere.  But the improved about:memory is a big part of that, and it won’t land until Firefox 6.  I’m wondering if the about:memory changes should be backported to Firefox 5 in an attempt to improve our leak reports ASAP.

Another thing I’m wondering about is being more aggressive about closing old leak reports.  We have 624 open bugs that have the “mlk” keyword (including some recent ones that aren’t in the list above).  The oldest of these is bug 39323 which was filed in May 2000.  Surely most of these aren’t relevant any more?  It’s good to have a mechanism for tracking leaks (be it a keyword or a tracking bug) but if most such bugs are never closed, the mechanism ends up being useless.  I’d love to hear ideas about this.

Finally, I’d like to hear if people think this blog post is useful;  I’m happy to make it an ongoing series if so, though regular MemShrink meetings would be more effective.

 

Categories
about:memory Firefox Memory consumption

A better about:memory: stage 1.75

I just landed bug 657327, which makes about:memory simpler and more useful.  To understand the change, let’s look at what about:memory looked like before the change landed.

Old about:memory screenshot

The first thing to look at is the “mapped” entry at the top of the “Mapped Memory” tree.  It was meant to measure the total memory (both private and shared) mapped by the process.  But there were a couple of problems with it:

  • On Windows, it only measured the private bytes.  There’s no easy way I know of to measure the shared bytes as well.  This could lead to negative numbers in the output (bug 655642).
  • On Mac, the number includes an enormous amount of shared memory mapped.  If you have a Mac, run ‘top’ and look at the VSIZE column.  Almost every process has a value of 2GB or greater.  So the “mapped” value is really high, which looks bad, even though it’s not Firefox’s fault.
  • Even on Linux, where the amount of shared memory is smaller and so the “mapped” number is reasonable, it’s still not that useful, because it includes memory mappings like code and data segments that aren’t that interesting.

So, in summary, the very first number shown on about:memory was (a) incorrect on Windows, (b) misleadingly inflated on Mac, and (c) not much use on Linux.

The other thing to notice about the old about:memory is that there are two trees, “Mapped Memory” and “Used Heap Memory”.  For the purposes of this discussion, memory usage can be split into four groups.

  1. Explicitly allocated heap memory.  This is heap memory requested by Firefox through the heap allocation functions like malloc, calloc, realloc, and C++’s ‘operator new’.
  2. Implicitly allocated heap memory.  This is heap memory that has been freed by Firefox through the heap deallocate functions like free and C++’s ‘operator delete’, but which the heap allocator (e.g. jemalloc) has not yet handed back to the OS, for whatever reason.
  3. Explicitly allocated mapped memory.  This is memory requested by Firefox through OS-level allocation functions like mmap (on Linux and Mac), VirtualAlloc (on Windows), and vm_allocate (on Mac).
  4. Implicitly allocated mapped memory.  This is memory allocated by the OS that hasn’t been explicitly requested by Firefox.  It includes code and data segments (which are created when the executable and shared libraries are loaded) and thread stacks (which are created when threads are created).

In the old about:memory, 1 is shown in the “Used Heap Memory” tree, and 2, 3 and 4 are shown in the “Mapped Memory”.  But it’s 1 and 3 that we’re most interested in, because that’s memory that has been explicitly requested (and not yet freed) by Firefox.  That’s where most of the dynamic variation in memory usage occurs, and that’s where memory leaks occur.

The new about:memory reflects this better.

New about:memory screenshot

It has a single tree which only includes explicit allocations, and which does not distinguish between heap-level allocations (e.g. malloc) and OS-level allocations (e.g. mmap);  this shortens the output and reduces the amount of nesting in the tree.  Implicit allocations (2 and 4 above) are still covered, but only in the less-prominent “Other Measurements” list (under “vsize” and “heap-unused”).  And the “explicit” entry, the very first one, is now the single most interesting number on the page.  (Thanks to Jesse Ruderman for suggesting that I merge the two trees and flatten the resulting tree.)

One disadvantage of the new form is that some explicit OS-level allocations may not be accounted for.  (The full heap is always accounted for, thankfully.)  I’m in the process of adding more memory reporters for significant OS-level allocations (e.g. bug 546477).  Fortunately there doesn’t seem to be many.

Categories
about:memory Firefox Memory consumption

A better about:memory: stage 1.5

I just landed a bunch of changes to about:memory (bug 648490, bug 653630, bug 654041, bug 655638, bug 655583).  Mostly they just fix some minor problems;  if you’ve seen negative numbers in about:memory since the revamp hopefully you won’t any more!  (Please tell me or file a bug if you do.)

But there’s one cool new feature:

GC buttons

At the bottom of about:memory there are now three buttons.  Here are the “title” attributes for each one, which show up as tool-tips if you hover your  mouse over them, and explain what they do.

  • GC: Do a global garbage collection.
  • GC + CC: Do a global garbage collection followed by a cycle collection. (It currently is not possible to do a cycle collection on its own, see bug 625302.)
  • Minimize memory usage: Send three “heap-minimize” notifications in a row.  Each notification triggers a global garbage collection followed by a cycle collection, and causes the process to reduce memory usage in other ways, e.g. by flushing various caches.

As far as I know this is the first time users have been able to trigger GC and CC easily in a vanilla browser.  It’ll be particularly useful when analyzing memory usage, e.g. trying to determine if there’s a leak.  Often in that case you want to trigger a GC and/or CC to make sure that the memory stats aren’t currently inflated by dead objects, and it’s now really easy to do so.

On a related note: it’s important that the memory reporters used to generate about:memory be correct, and that the memory be categorized the right way.  (Otherwise you can end up with nonsensical output like the negative numbers I mentioned earlier.)  For example, I just discovered that the JavaScript heap can be allocated on the heap or directly via mmap/VirtualAlloc, depending on whether MOZ_MEMORY is defined or not (see bug 656520).  On Mac, MOZ_MEMORY is not defined (because it currently doesn’t use jemalloc) and so the GC heap was incorrectly being categorized under “Used Heap Memory” instead of the “Mapped Memory”.

I’ve checked all the reporters as best as I can.  I’m pretty confident now that all the JS and storage (SQLite) reporters are correctly categorized as “heap” or “mapped”.  I’ve looked at the others and I think they’re right, but I’m not totally certain.  More specifically, the reporters in the following screenshot are currently categorized as “heap” (i.e. allocated with oe of: malloc, calloc, realloc, posix_memalign, operator new, operator new[]).  If anyone knows that to be false, I’d love to hear about it.  In particular, I’m worried about image-related memory that might be stored in video RAM;  I already adjusted two reporters (gfx-2d-surfacecache and gfx-2d-surfacevram) for this reason.

about:memory screenshot
Thanks!