devtools-docs/docs/heap-profiling.html

329 строки
13 KiB
HTML

{{+bindTo:partials.standard_devtools_article}}
<h1>Profiling memory performance</h1>
<p>This tutorial explains how to use the Heap Profiler for uncovering
memory leaks in your applications.</p>
<div class="special"><p>Read
the <a href="memory-analysis-101.html">Memory 101</a> page to become
familiar with the terms used in this document.</p></div>
<p class="note"><b>Note:</b> If you are a Web Developer and want to get the
latest version of Developer Tools, you should use the Google Chrome release
from the <a href="http://dev.chromium.org/getting-involved/dev-channel">Developer Channel</a>.
</p>
<div class="collapsible">
<h2 id="basics">Basic functions</h2>
<h3 id="basics_snapshot">Taking a snapshot</h3>
<p>On the <strong>Profiles</strong> tab, select the <strong>Take Heap
Snapshot</strong> option and press <strong>Start</strong>:
<div class="screenshot"><img src="heap-profiling-files/take_snapshot_button.png"></div>
<p>Snapshots are initially stored in the renderer process memory. They are
transferred to the DevTools on demand, when you click on the snapshot icon to
view it. After the snapshot has been loaded into DevTools and has been parsed,
the number below the snapshot title appears and shows the total size of
the <a href="memory-analysis-101.html#retaining_paths">reachable</a> JavaScript
objects:</p>
<div class="screenshot"><img src="heap-profiling-files/snapshot_memory.png"></div>
<h3 id="basics_clear">Clearing snapshots</h3>
<p>Snapshots can be removed (both from DevTools and renderer's memory) by
pressing the <strong>Clear all profiles</strong> button:</p>
<div class="screenshot"><img src="heap-profiling-files/clear_profiles.png"></div>
<p class="note"><b>Note:</b> Closing the DevTools window will not delete
collected profiles from the renderer's memory. When reopening DevTools, all
previously taken snapshots will reappear in the list of snapshots.</p>
<h3 id="basics_views">Switching between snapshot views</h3>
<p>A snapshot can be viewed from different perspectives for different tasks. To
switch between views, use the selector at the bottom of the view:</p>
<div class="screenshot"><img src="heap-profiling-files/view_selector_crop.png"></div>
<p>There are four views:
<ul>
<li><strong>Summary</strong> &mdash; shows objects grouped by
the constructor name;</li>
<li><strong>Comparison</strong> &mdash; displays difference between
two snapshots;</li>
<li><strong>Containment</strong> &mdash; allows exploration of heap
contents;</li>
<li><strong>Dominators</strong> &mdash;
shows <a href="memory-analysis-101.html#dominators">dominators
tree.</a>
</li>
</ul>
<h3 id="basics_colors">Looking up color coding</h3>
<p>Properties and property values of objects have different types and
are colored accordingly. To view the type color legend, press the
question mark button on the status bar:</p>
<div class="screenshot"><img src="heap-profiling-files/colors_legend.png"></div>
<p>Each property has one of four types::
<ul>
<li><strong>property</strong> &mdash; a regular property with a name,
accessed via the <code>.</code> (dot) operator, or via
<code>[]</code> (brackets) notation, e.g. <code>["foo bar"]</code>;</li>
<li><strong>element</strong> &mdash; a regular property with a numeric
index, accessed via <code>[]</code> (brackets) notation;</li>
<li><strong>context variable</strong> &mdash; a variable in a function
context, accessible by its name from inside a function closure;</li>
<li><strong>system property</strong> &mdash; property added by the
JavaScript VM, not accessible from JavaScript code.</li>
</ul>
</p>
<p>Objects designated as <strong>System</strong> don't have
corresponding JavaScript types. They are part of JavaScript VM's
object system implementation.</p>
</div>
<div class="collapsible">
<h2 id="views">Views in detail</h2>
<h3 id="views_summary">Summary view</h3>
<p>Initially, a snapshot opens in the <strong>Summary</strong> view, displaying
object totals, which can be expanded to show instances:</p>
<div class="screenshot"><img src="heap-profiling-files/summary_view.png"></div>
<p>Top-level entries are &quot;total&quot; lines. They display the
constructor name, and group all objects created using this
constructor. The number of object instances is displayed in
the <strong>#</strong> column. The <strong>Shallow size</strong>
column displays the sum
of <a href="memory-analysis-101.html#object_sizes">shallow sizes</a>
of all objects created by a certain constructor
function. The <strong>Retained Size</strong> column displays the
maximum retained size among the same set of objects.</p>
<p>After expanding a total line in the upper view, all of its
instances are displayed. For each instance, its shallow and retained
sizes are displayed in the corresponding columns. The number after
the <strong>@</strong> character is the object unique ID, allowing to
compare heap snapshots on per-object basis.</p>
<p>Try this <a href="heap-profiling-summary.html" target="_blank">demo
page</a> (opens in a new tab) to understand how
the <strong>Summary</strong> view can be used.</p>
<h3 id="views_comparison">Comparison view</h3>
<p>When more than one heap snapshot is taken, it is possible to
compare them, and find leaked objects. To verify that a certain
application operation doesn't create leaks (e.g. usually a pair of
direct and reverse operations, like opening a document, and then
closing it, should not leave any garbage), you may follow the scenario
below:
<ol>
<li>take a heap snapshot before performing an operation;</li>
<li>perform an operation (e.g. open a document);</li>
<li>perform a reverse operation (close the document);</li>
<li>take a heap snapshot.</li>
</ol></p>
<p>In the <strong>Comparison</strong> view, the difference between two
snapshots is displayed. When expanding a total entry, added and
deleted object instances are shown:</p>
<div class="screenshot"><img src="heap-profiling-files/comparison_view.png"></div>
<p>Try this <a href="heap-profiling-comparison.html" target="_blank">demo
page</a> (opens in a new tab) to get an idea how to use snapshot comparison for
detecting leaks.</p>
<h3 id="views_containment">Containment view</h3>
<p>The <strong>Containment</strong> view is essentially a &quot;bird's eye
view&quot; of your application's objects structure. It allows you to peek inside
function closures, to observe VM internal objects that together make up your
JavaScript objects, and to understand how much memory your application uses at a
very low level.</p>
<p>The view provides several entry points:
<ul>
<li><code>DOMWindow</code> objects &mdash; these are objects
considered as &quot;global&quot; objects for JavaScript code;</li>
<li>GC roots &mdash; actual GC roots used by VM's garbage collector;</li>
<li>Native objects &mdash; browser objects that are &quot;pushed&quot;
inside the JavaScript virtual machine to allow automation, e.g. DOM nodes,
CSS rules (see the next section for more details.)</li>
</ul>
<p>Below is the example of what the <strong>Containment</strong> view
looks like:</p>
<div class="screenshot"><img src="heap-profiling-files/containment_view.png"></div>
<p>Try this <a href="heap-profiling-containment.html"
target="_blank">demo page</a> (opens in a new tab) for finding out how
to explore closures and event handlers using the view.</p>
<h3 id="views_dom">Uncovering DOM leaks</h3>
<p>The unique ability of the new tool is to reflect bidirectional
dependencies between browser native objects (DOM nodes, CSS rules) and
JavaScript objects. This helps to discover otherwise invisible leaks
happening due to forgotten detached DOM subtrees floating around.</p>
<p>Native objects are most easily accessible
from <strong>Summary</strong> and <strong>Containment</strong> views
&mdash; there are dedicated entry nodes for them:</p>
<div class="screenshot"><img src="heap-profiling-files/dom_leaks_summary.png"></div>
<p>Try this <a href="heap-profiling-dom-leaks.html"
target="_blank">demo page</a> (opens in a new tab) to play with
detached DOM trees.</p>
<h3 id="views_dominators">Dominators view</h3>
<p>The <strong>Dominators</strong> view shows the dominators tree for
the heap graph. The <strong>Dominators</strong> view looks similar to
the <strong>Containment</strong> view, but lacks property names. This
is because a dominator of an object may lack direct references to it,
that is, the dominators tree is not a spanning tree of the graph. But
this only serves for good, as helps us to identify memory accumulation
points quickly.</p>
<div class="screenshot"><img src="heap-profiling-files/dominators_view.png"></div>
<p>Try this <a href="heap-profiling-dominators.html"
target="_blank">demo page</a> (opens in a new tab) to train yourself
in finding accumulation points.</p>
<h3 id="views_paths">Retaining paths</h3>
<p>The retaining paths view is always displayed. To activate it, click on an
object in the upper pane, regardless of what view is currently active. The
profiler will start looking up for simple paths from roots to the object
selected. It will start from shorter paths, gradually increasing walking
distance. It is possible to restrict the roots set to <code>DOMWindow</code>
objects, instead of all GC roots. This is helpful for finding references
originating from the application itself &mdash; they are the most probable
sources for leaks:
<div class="screenshot"><img src="heap-profiling-files/retaining_paths_crop.png"></div>
<p>In the simplest case, the path will only include &quot;true&quot;
JavaScript objects and use references that are accessible from
JavaScript code, e.g.:
<ul>
<li><code>DOMWindow@1234.foo.bar</code></li>
<li><code>DOMWindow@1235.listeners[34].handler["on click event"]</code></li>
</ul>
</p>
<p>Such paths can be easily evaluated in the debugger console if the appropriate
context has been entered. For example, if an application has several iframes,
execution must be suspended during running a function code belonging to
it). Evaluating a path makes it possible to examine the complete contents of
each object, including properties whose values not stored in the heap, and thus
not captured in a snapshot.</p>
<p>Often it happens that an object is retained via closures. Closure references
are displayed using the <code>-&gt;</code> notation:
<ul>
<li><code>DOMWindow@1234.callbacks[12]->context</code></li>
<li><code>DOMWindow@1235.callback->nested->inside</code></li>
</ul>
Such paths can't be evaluated in the console.</p>
<p>Even more advanced are paths including &quot;hidden&quot;
objects. Normally, the existence of such paths doesn't mean that an
application has a leak. Some of the paths are &quot;low level&quot;
paths, equivalent to their JavaScript shortcuts. E.g., every object
has <code>elements</code> and <code>properties</code> internal arrays,
thus a property value can be displayed as held directly by an object
(this is how a JavaScript programmer sees it), or as an element of the
<code>properties</code> array (this is how the JavaScript virtual machine
actually accesses it.) Knowing these details might help in getting an
understanding of how the virtual machine works. However, for most of web
application developers it is safe to ignore them. Below are some examples of
&quot;internal&quot; paths:
<ul>
<li><code>GCRoots@1[254]</code></li>
<li><code>DOMWindow@1234{prop}{1}</code> &mdash; this is equivalent to a JavaScript path: <code>DOMWindow@1234.prop</code></li>
<li><code>DOMWindow@1235.object{properties}{2}</code></li>
</ul>
</p>
</div>
<div class="collapsible">
<h2 id="qna">Q&amp;A</h2>
<dl>
<dt>I don't see all the properties of objects, I don't see
non-string values for them! Why?
<dd>Not all properties are actually stored on the JavaScript
heap. Some of them are implemented using getters that execute native
code. Such properties are not captured in heap snapshots in order to avoid
the cost of calling getters and to avoid possible program state
changes if getters are not &quot;pure&quot; functions. Also,
non-string values such as numbers are not captured in an attempt to
reduce snapshot size.</dd>
</dt>
</dl>
<dl>
<dt>What does the number after
the <code>@</code> char mean &mdash; is this an address or an ID?
Is the ID value really unique?
<dd>This is an object ID. Displaying an object's address makes no
sense, as objects are moved during garbage collections. Those
object IDs are real IDs &mdash; that means, they persist among
multiple snapshots taken. This allows precise comparison between
heap states. Maintaining those IDs adds an overhead to GC cycles,
but it is only initiated after the first heap snapshot was taken
&mdash; no overhead if heap profiles aren't used.</dd>
</dt>
</dl>
<dl>
<dt>Are &quot;dead&quot; (unreachable) objects
included in snapshots?
<dd>No. Only reachable objects are included in snapshots. Also,
taking a snapshot always starts with doing a GC.</dd>
</dt>
</dl>
<dl>
<dt>What comprises GC roots?
<dd>Many things:
<ul>
<li>built-in object maps;</li>
<li>symbol table;</li>
<li>stacks of VM threads;</li>
<li>compilation cache;</li>
<li>handle scopes;</li>
<li>global handles.</li>
</ul>
</dd>
</dt>
</dl>
</div>
{{/partials.standard_devtools_article}}