329 строки
13 KiB
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> — shows objects grouped by
|
|
the constructor name;</li>
|
|
<li><strong>Comparison</strong> — displays difference between
|
|
two snapshots;</li>
|
|
<li><strong>Containment</strong> — allows exploration of heap
|
|
contents;</li>
|
|
<li><strong>Dominators</strong> —
|
|
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> — 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> — a regular property with a numeric
|
|
index, accessed via <code>[]</code> (brackets) notation;</li>
|
|
<li><strong>context variable</strong> — a variable in a function
|
|
context, accessible by its name from inside a function closure;</li>
|
|
<li><strong>system property</strong> — 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 "total" 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 "bird's eye
|
|
view" 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 — these are objects
|
|
considered as "global" objects for JavaScript code;</li>
|
|
<li>GC roots — actual GC roots used by VM's garbage collector;</li>
|
|
<li>Native objects — browser objects that are "pushed"
|
|
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
|
|
— 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 — 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 "true"
|
|
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>-></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 "hidden"
|
|
objects. Normally, the existence of such paths doesn't mean that an
|
|
application has a leak. Some of the paths are "low level"
|
|
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
|
|
"internal" paths:
|
|
<ul>
|
|
<li><code>GCRoots@1[254]</code></li>
|
|
<li><code>DOMWindow@1234{prop}{1}</code> — 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&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 "pure" 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 — 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 — 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
|
|
— no overhead if heap profiles aren't used.</dd>
|
|
</dt>
|
|
</dl>
|
|
|
|
<dl>
|
|
<dt>Are "dead" (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}}
|