{"id":2970,"date":"2014-08-29T15:17:34","date_gmt":"2014-08-29T04:17:34","guid":{"rendered":"http:\/\/blog.mozilla.org\/nnethercote\/?p=2970"},"modified":"2014-08-29T15:17:34","modified_gmt":"2014-08-29T04:17:34","slug":"per-class-js-object-and-shape-measurements-in-firefoxs-aboutmemory","status":"publish","type":"post","link":"https:\/\/blog.mozilla.org\/nnethercote\/2014\/08\/29\/per-class-js-object-and-shape-measurements-in-firefoxs-aboutmemory\/","title":{"rendered":"Per-class JS object and shape measurements in Firefox&#8217;s about:memory"},"content":{"rendered":"<p>A few days ago I landed support for <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=1023719\">per-class reporting<\/a> of JavaScript objects and shapes in about:memory. (Shapes are auxiliary, engine-internal data structures that are used to facilitate object property accesses. They can use large amounts of memory.)<\/p>\n<p>Prior to this patch, the JavaScript objects and shapes within a single compartment (which corresponds to a JavaScript <code>window<\/code> or <code>global<\/code> object) would be covered by measurements in a small number of fixed categories.<\/p>\n<pre>10,179,152 B (02.59%) -- objects\r\n\u251c\u2500\u2500\u25006,749,600 B (01.72%) -- gc-heap\r\n\u2502   \u251c\u2500\u25003,512,640 B (00.89%) \u2500\u2500 dense-array\r\n\u2502   \u251c\u2500\u25002,965,184 B (00.75%) \u2500\u2500 ordinary\r\n\u2502   \u2514\u2500\u2500\u2500\u2500271,776 B (00.07%) \u2500\u2500 function\r\n\u251c\u2500\u2500\u25003,429,552 B (00.87%) -- malloc-heap\r\n\u2502   \u251c\u2500\u25002,377,600 B (00.61%) \u2500\u2500 slots\r\n\u2502   \u2514\u2500\u25001,051,952 B (00.27%) \u2500\u2500 elements\/non-asm.js\r\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25000 B (00.00%) \u2500\u2500 non-heap\/code\/asm.js\r\n474,144 B (00.12%) -- shapes\r\n\u251c\u2500\u2500316,832 B (00.08%) -- gc-heap\r\n\u2502  \u251c\u2500\u2500167,320 B (00.04%) -- tree\r\n\u2502  \u2502  \u251c\u2500\u2500152,400 B (00.04%) \u2500\u2500 global-parented\r\n\u2502  \u2502  \u2514\u2500\u2500\u250014,920 B (00.00%) \u2500\u2500 non-global-parented\r\n\u2502  \u251c\u2500\u2500125,352 B (00.03%) \u2500\u2500 base\r\n\u2502  \u2514\u2500\u2500\u250024,160 B (00.01%) \u2500\u2500 dict\r\n\u2514\u2500\u2500157,312 B (00.04%) -- malloc-heap\r\n   \u251c\u2500\u2500\u250099,328 B (00.03%) \u2500\u2500 compartment-tables\r\n   \u251c\u2500\u2500\u250035,040 B (00.01%) \u2500\u2500 tree-tables\r\n   \u251c\u2500\u2500\u250012,704 B (00.00%) \u2500\u2500 dict-tables\r\n   \u2514\u2500\u2500\u250010,240 B (00.00%) \u2500\u2500 tree-shape-kids<\/pre>\n<p>These measurements are only interesting to those who understand the guts of the JavaScript engine.<\/p>\n<p>In contrast, objects and shapes are now grouped by their class. Per-class measurements relate back to the JavaScript code in a more obvious way, making these measurements useful to a wider range of people.<\/p>\n<pre>10,515,296 B (02.69%) -- classes\r\n\u251c\u2500\u2500\u25004,566,840 B (01.17%) ++ class(Array)\r\n\u251c\u2500\u2500\u25003,618,464 B (00.93%) ++ class(Object)\r\n\u251c\u2500\u2500\u25001,755,232 B (00.45%) ++ class(HTMLDivElement)\r\n\u251c\u2500\u2500\u2500\u2500\u2500333,624 B (00.09%) ++ class(Function)\r\n\u251c\u2500\u2500\u2500\u2500\u2500165,624 B (00.04%) ++ class(&lt;non-notable classes&gt;)\r\n\u251c\u2500\u2500\u2500\u2500\u2500\u250038,736 B (00.01%) ++ class(Window)\r\n\u2514\u2500\u2500\u2500\u2500\u2500\u250036,776 B (00.01%) ++ class(CSS2PropertiesPrototype)<\/pre>\n<p>(The <code>&lt;non-notable classes&gt;<\/code> entry aggregates all classes that are smaller than a certain threshold. This prevents any long tail of classes from bloating about:memory too much.)<\/p>\n<p>Expanding the sub-tree for the <code>Object<\/code> class, we see that the fixed categories are still present, for those who are interested in them.<\/p>\n<pre>3,618,464 B (00.93%) -- class(Object)\r\n\u251c\u2500\u25003,540,672 B (00.91%) -- objects\r\n\u2502  \u251c\u2500\u25002,349,632 B (00.60%) -- malloc-heap\r\n\u2502  \u2502  \u251c\u2500\u25002,348,480 B (00.60%) \u2500\u2500 slots\r\n\u2502  \u2502  \u2514\u2500\u2500\u2500\u2500\u2500\u25001,152 B (00.00%) \u2500\u2500 elements\/non-asm.js\r\n\u2502  \u2514\u2500\u25001,191,040 B (00.30%) \u2500\u2500 gc-heap\r\n\u2514\u2500\u2500\u2500\u2500\u250077,792 B (00.02%) -- shapes\r\n      \u251c\u2500\u250057,376 B (00.01%) -- gc-heap\r\n      \u2502  \u251c\u2500\u250047,120 B (00.01%) \u2500\u2500 tree\r\n      \u2502  \u251c\u2500\u2500\u25005,360 B (00.00%) \u2500\u2500 dict\r\n      \u2502  \u2514\u2500\u2500\u25004,896 B (00.00%) \u2500\u2500 base\r\n      \u2514\u2500\u250020,416 B (00.01%) -- malloc-heap\r\n         \u251c\u2500\u250011,552 B (00.00%) \u2500\u2500 tree-tables\r\n         \u251c\u2500\u2500\u25006,912 B (00.00%) \u2500\u2500 tree-kids\r\n         \u2514\u2500\u2500\u25001,952 B (00.00%) \u2500\u2500 dict-tables<\/pre>\n<p>Although the per-class measurements often aren&#8217;t surprising &#8212; <code>Object<\/code> and <code>Array<\/code> objects and shapes often dominate &#8212; sometimes they are. Consider the following examples.<\/p>\n<ul>\n<li>The above example has 1.7 MiB of <code>HTMLDivElement<\/code> objects and shapes, which indicates that the compartment contains many <code>div<\/code> elements.<\/li>\n<li>If you have lots of memory used by <code>Function<\/code> objects and shapes, it suggests that the code is creating excessive numbers of closures.<\/li>\n<li>Just this morning a visitor to the #memshrink IRC channel was wondering why they had 11 MiB of <code>XPC_WN_NoMods_NoCall_Proto_JSClass<\/code> objects and shapes in one compartment. (This is a question I currently don&#8217;t have a good answer for.)<\/li>\n<\/ul>\n<p>Historically, the data-dependent measurements in about:memory &#8212; e.g. those done on a per-tab, or per-compartment, or per-image, or per-script basis &#8212; have been more useful and interesting than the ones in fixed categories, because they map obviously to browser and code artifacts. For example, per-tab measurements let you know if a particular web page is using excessive memory, and per-compartment measurements revealed the existence of <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Zombie_compartments\">zombie compartments<\/a>, a kind of bad memory leak that used to be common in <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=668871\">Firefox<\/a> and its\u00a0<a href=\"https:\/\/blog.mozilla.org\/nnethercote\/2012\/07\/19\/firefox-15-plugs-the-add-on-leaks\/\">add-ons<\/a>.<\/p>\n<p>I&#8217;m hoping that these per-class measurements will prove similarly useful. Keep an eye on them, and please let me know and\/or file bugs if you see any surprising cases.<\/p>\n<p>A final note: Mozilla&#8217;s devtools team is currently making great progress on a JavaScript memory profiler, which will give finer-grained measurements of JavaScript memory usage in web content. Although there will be some overlap between that tool and these new measurements in about:memory, it will useful to have both tools, because each one will be appropriate in different circumstances.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A few days ago I landed support for per-class reporting of JavaScript objects and shapes in about:memory. (Shapes are auxiliary, engine-internal data structures that are used to facilitate object property accesses. They can use large amounts of memory.) Prior to this patch, the JavaScript objects and shapes within a single compartment (which corresponds to a [&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,4546],"tags":[],"_links":{"self":[{"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/posts\/2970"}],"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=2970"}],"version-history":[{"count":0,"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/posts\/2970\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/media?parent=2970"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/categories?post=2970"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/tags?post=2970"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}