{"id":1369,"date":"2011-11-01T13:06:51","date_gmt":"2011-11-01T02:06:51","guid":{"rendered":"http:\/\/blog.mozilla.org\/nnethercote\/?p=1369"},"modified":"2011-11-01T16:34:05","modified_gmt":"2011-11-01T05:34:05","slug":"spidermonkey-is-on-a-diet","status":"publish","type":"post","link":"https:\/\/blog.mozilla.org\/nnethercote\/2011\/11\/01\/spidermonkey-is-on-a-diet\/","title":{"rendered":"SpiderMonkey is on a diet"},"content":{"rendered":"<p>One thing I&#8217;ve learnt while working for Mozilla is that a web browser can be characterized as a JavaScript execution environment that happens to have some multimedia capabilities.\u00a0 In particular, if you look at Firefox&#8217;s about:memory page, the JS engine is very often the component responsible for consuming the most memory.<\/p>\n<p>Consider the following snapshot from about:memory of the memory used by a single JavaScript <a href=\"http:\/\/andreasgal.com\/2010\/10\/13\/compartments\/\">compartment<\/a>.<\/p>\n<p><a href=\"http:\/\/blog.mozilla.org\/nnethercote\/files\/2011\/10\/js-compartment.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-1371\" title=\"js-compartment\" src=\"http:\/\/blog.mozilla.org\/nnethercote\/files\/2011\/10\/js-compartment.png\" alt=\"about:memory snapshot\" width=\"694\" height=\"686\" srcset=\"https:\/\/blog.mozilla.org\/nnethercote\/files\/2011\/10\/js-compartment.png 694w, https:\/\/blog.mozilla.org\/nnethercote\/files\/2011\/10\/js-compartment-300x296.png 300w\" sizes=\"(max-width: 694px) 100vw, 694px\" \/><\/a><\/p>\n<p>(For those of you who have looked at about:memory before, some of those entries may look unfamiliar, because I landed a <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=697016\">patch to refine the JS memory reporters<\/a> late last week.)<\/p>\n<p>There is work underway to reduce many of the entries in that snapshot.\u00a0 SpiderMonkey is on a diet.<\/p>\n<h3>Objects<\/h3>\n<p>Objects are the primary data structure used in JS programs;\u00a0 after all, it is an object-oriented language.\u00a0 Inside SpiderMonkey, each object is represented by a JSObject, which holds basic information, and possibly a slots array, which holds the object&#8217;s properties. The memory consumption for all JSObjects is measured by the &#8220;gc-heap\/objects\/non-function&#8221; and &#8220;gc-heap\/objects\/function&#8221; entries in about:memory, and the slots arrays are measured by the &#8220;object-slots&#8221; entries.<\/p>\n<p>The size of a non-function JSObject is currently 40 bytes on 32-bit platforms and 72 bytes on 64-bit platforms.\u00a0 Brian Hackett is <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=637931\">working<\/a> to <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=687788\">reduce that to 16 bytes and 32 bytes respectively<\/a>. Function JSObjects are a little larger, being (internally) a sub-class of JSObject called JSFunction.\u00a0 JSFunctions will therefore benefit from the shrinking of JSObject, and Brian is <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=697537\">slimming down the function-specific parts<\/a> as well.\u00a0 In fact, these changes are complete in the <a href=\"http:\/\/hg.mozilla.org\/projects\/jaegermonkey\">JaegerMonkey repository<\/a>, and will likely be merged into mozilla-central early in the Firefox 11 development period.<\/p>\n<p>As for the slots arrays, they are currently arrays of &#8220;fatvals&#8221; A fatval is a 64-bit internal representation that can hold any JS value &#8212; number, object, string, whatever.\u00a0 (See <a href=\"http:\/\/evilpie.github.com\/sayrer-fatval-backup\/cache.aspx.htm\">here<\/a> for details, scroll down to &#8220;Mozilla\u2019s New JavaScript Value Representation&#8221;;\u00a0 the original blog entry is apparently no longer available).\u00a0 64-bits per entry is overkill if you know, for example, that you have an array full entirely of integers that could fit into 32 bits.\u00a0 Luke Wagner and Brian Hackett have been <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=689745\">discussing a specialized representation<\/a> to take advantage of such cases.\u00a0 Variations on this idea have been tried <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=486356\">twice<\/a> <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=566767\">before<\/a> and failed, but perhaps SpiderMonkey&#8217;s new type inference support will provide the right infrastructure for it to happen.<\/p>\n<h3>Shapes<\/h3>\n<p>There are a number of data structures within SpiderMonkey dedicated to making object property accesses fast.\u00a0 The most important of these are Shapes.\u00a0 Each Shape corresponds to a particular property that is present in one or more JS objects.\u00a0 Furthermore, Shapes are linked into linear sequences called &#8220;shape lineages&#8221;, which describe object layouts.\u00a0 Some shape lineages are shared and live in &#8220;property trees&#8221;.\u00a0 Other shape lineages are unshared and belong to a single JS object;\u00a0 these are &#8220;in dictionary mode&#8221;.<\/p>\n<p>The &#8220;shapes\/tree&#8221; and &#8220;shapes\/dict&#8221; entries in about:memory measure the memory consumption for all Shapes.\u00a0 Shapes of both kinds are the same size;\u00a0 currently they are 40 bytes on 32-bit platforms and 64 bytes on 64-bit platforms.\u00a0 But Brian Hackett has also been taking a hatchet to Shape, <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=687788\">reducing them to 24 bytes and 40 bytes<\/a> respectively.\u00a0 This has required the creation of a new auxiliary BaseShape type, but there should be many fewer BaseShapes than there are Shapes.\u00a0 This change will also increase the number of Shapes, but should result in a space saving overall.<\/p>\n<p>SpiderMonkey often has to search shape lineages, and for lineages that are hot it creates an auxiliary hash table, called a &#8220;property table&#8221;, that makes lookups faster.\u00a0 The &#8220;shapes-extra\/tree-tables&#8221; and &#8220;shapes-extra\/dict-tables&#8221; entries in about:memory measure these tables.\u00a0 Last Friday I landed a patch that <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=697646\">avoids building these tables if they only have a few items in them<\/a>;\u00a0 in that case a linear search is just as good.\u00a0 This reduced the amount of memory consumed by property tables by about 20%.<\/p>\n<p>I mentioned that many Shapes are in property trees.\u00a0 These are N-ary trees, but most Shapes in them have zero or one child;\u00a0 only a small fraction have more than that, but the maximum N can be hundreds or even thousands.\u00a0 So there&#8217;s a long-standing space optimization where each shape contains (via a union) a single Shape pointer which is used if it has zero or one child.\u00a0 But if the number of children increases to 2 or more, this is changed into a pointer to a hash table, which contains pointers to the N children.\u00a0 Until recently, if a Shape had a child deleted and that reduced the number of children from 2 to 1, it wouldn&#8217;t be converted from the hash form back to the single-pointer.\u00a0 I <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=697931\">changed this<\/a> last Friday.\u00a0 I also <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=697933\">reduced the minimum size of these hash tables<\/a> from 16 to 4, which saves a lot of space because most of them only have 2 or 3 entries.\u00a0 These two changes together reduced the size of the &#8220;shapes-extra\/tree-shape-kids&#8221; entry in about:memory by roughly 30&#8211;50%.<\/p>\n<h3>Scripts<\/h3>\n<p>Internally, a JSScript represents (more or less) the code of a JS function, including things like the internal bytecode that SpiderMonkey generates for it.\u00a0 The memory used by JSScripts is measured by the &#8220;gc-heap\/scripts&#8221; and &#8220;script-data&#8221; entries in about:memory.<\/p>\n<p>Luke Wagner did some measurements recently that showed that <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=678037\">most (70&#8211;80%) JSScripts created in the browser are never run<\/a>.\u00a0 In hindsight, this isn&#8217;t so surprising &#8212; many websites load libraries like jQuery but only use a fraction of the functions in those libraries.\u00a0 It wouldn&#8217;t be easy, but if SpiderMonkey could be changed to generate bytecode for scripts lazily, it could reduce &#8220;script-data&#8221; memory usage by 60&#8211;70%, as well as shaving non-trivial amounts of time when rendering pages.<\/p>\n<h3>Trace JIT<\/h3>\n<p>TraceMonkey is SpiderMonkey&#8217;s original JIT compiler, which was introduced in Firefox 3.5.\u00a0 Its memory consumption is measured by the &#8220;tjit-*&#8221; entries in about:memory.<\/p>\n<p>With the improvements that type inference made to JaegerMonkey, TraceMonkey simply isn&#8217;t needed any more.\u00a0 Furthermore, it&#8217;s a big hairball that few if any JS team members will be sad to say goodbye to.\u00a0 (js\/src\/jstracer.cpp alone is over 17,000 lines and over half a megabyte of code!)<\/p>\n<p>TraceMonkey was turned off for web content JS code when type inference landed.\u00a0 And then it was <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=693815\">turned off for chrome code<\/a>.\u00a0 And now it is <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=697666\">not even built by default<\/a>.\u00a0 (The about:memory snapshot above was from a build just before it was turned off.)\u00a0 And it will be <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=698201\">removed entirely<\/a> early in the Firefox 11 development period.<\/p>\n<p>As well as saving memory for trace JIT code and data (including the wasteful <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=624590\">ballast hack<\/a> required to avoid OOM crashes in Nanojit, ugh), removing all that code will significantly shrink the size of Firefox&#8217;s code.\u00a0 David Anderson told me the binary of the standalone JS shell is about 0.5MB smaller with the trace JIT removed.<\/p>\n<h3>Method JIT<\/h3>\n<p>JaegerMonkey is SpiderMonkey&#8217;s second JIT compiler, which was introduced in Firefox 4.0.\u00a0 Its memory consumption is measured by the &#8220;mjit-code\/*&#8221; and &#8220;mjit-data&#8221; entries in about:memory.<\/p>\n<p>JaegerMonkey generates a lot of code.\u00a0 This situation will hopefully improve with the introduction of <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=650180\">IonMonkey<\/a>, which is SpiderMonkey&#8217;s third JIT compiler.\u00a0 IonMonkey is still in early development and won&#8217;t be integrated for some time, but it should generate code that is not only much faster, but much smaller.<\/p>\n<h3>GC HEAP<\/h3>\n<p>There is a great deal of work being done on the JS garbage collector, by Bill McCloskey, Chris Leary, Terrence Cole, and others.\u00a0 I&#8217;ll just point out two long-term goals that should reduce memory consumption significantly.<\/p>\n<p>First, the JS heap currently has a great deal of wasted space due to fragmentation, i.e. intermingling of used and unused memory.\u00a0 Once <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=650161\">moving GC<\/a> &#8212; i.e. the ability to move things on the heap &#8212; is implemented, it will pave the way for a compacting GC, which is one that can move live things that are intermingled with unused memory into contiguous chunks of memory.\u00a0 This is a challenging goal, especially given Firefox&#8217;s high level of interaction between JS and C++ code (because moving C++ objects is not feasible), but one that could result in very large savings, greatly reducing the &#8220;gc-heap\/arena\/unused&#8221; and &#8220;gc-heap-chunk-*-unused&#8221; measurements in about:memory.<\/p>\n<p>Second, a moving GC is a prerequisite for a <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=619558\">generational GC<\/a>, which allocates new things in a small chunk of memory called a &#8220;nursery&#8221;.\u00a0 The nursery is garbage-collected frequently (this is cheap because it&#8217;s small), and objects in the nursery that survive a collection are promoted to a &#8220;tenured generation&#8221;.\u00a0 Generational GC is a win because in practice the majority of things allocated die quickly and are not promoted to the tenured generation.\u00a0 This means the heap will grow more slowly.<\/p>\n<h3>Is that all?<\/h3>\n<p>It&#8217;s all I can think of right now.\u00a0 If I&#8217;ve missed anything, please add details in the comments.<\/p>\n<p>There&#8217;s an incredible amount of work being done on SpiderMonkey at the moment, and a lot of it will help reduce Firefox&#8217;s memory consumption.\u00a0 I can&#8217;t wait to see what SpiderMonkey looks like in 6 months!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>One thing I&#8217;ve learnt while working for Mozilla is that a web browser can be characterized as a JavaScript execution environment that happens to have some multimedia capabilities.\u00a0 In particular, if you look at Firefox&#8217;s about:memory page, the JS engine is very often the component responsible for consuming the most memory. Consider the following snapshot [&hellip;]<\/p>\n","protected":false},"author":139,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4550,30,4555,4543,4544,4546,467],"tags":[],"_links":{"self":[{"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/posts\/1369"}],"collection":[{"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/users\/139"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/comments?post=1369"}],"version-history":[{"count":0,"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/posts\/1369\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/media?parent=1369"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/categories?post=1369"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/tags?post=1369"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}