As of Chrome 48, MemoryInfra supports heap profiling. Chrome will track all live allocations (calls to new or malloc without a subsequent call to delete or free) along with sufficient metadata to identify the code that made the allocation.
Navigate to chrome://flags and search for
Choose the process types you want to profile with the
memlog flag. The most common setting is
You can control resolution of sampling with
memlog-sampling-rate. By default samples are collected with the average interval of 100KB. On versions prior Chrome 75 you can enable
memlog-keep-small-allocations to record all the allocations, however this option has a significant performance and memory overhead.
By default, stack traces use native stack traces, which does not contain any thread information. To include the thread at time of allocation, set
native with thread names.
Grab a MemoryInfra trace.
Save the trace.
To symbolize the trace:
addr2line-pdbfrom the chromium repository. For subsequent commands, add the flag
./third_party/catapult/tracing/bin/symbolize_trace --is-local-build <path_to_trace>
./third_party/catapult/tracing/bin/symbolize_trace <path_to_trace>. This will request authentication with google cloud storage to obtain symbol files [googlers only].
Turn off heap profiling in chrome://flags. Restart Chrome.
Load the (now symbolized) trace in chrome://tracing.
To obtain native heap dumps, you will need a custom build of Chrome with the GN arguments
enable_profiling = true,
arm_use_thumb = false and
symbol_level=1. All other steps are the same.
Alternatively, if you want to use an Official build of Chrome, navigate to chrome://flags and set
pseudo. This will provide less-detailed stacks. The stacks also don't require symbolization.
For the most part, the setting
chrome://flags has a similar effect to the various
Select a heavy memory dump indicated by a purple dot.
In the analysis view, cells marked with a triple bar icon (☰) contain heap dumps. Select such a cell.
Scroll down all the way to Heap Details.
To navigate allocations, select a frame in the right-side pane and press Enter/Return. To pop up the stack, press Backspace/Delete.
python ./third_party/catapult/experimental/tracing/bin/diff_heap_profiler.py <path_to_trace>
This produces a directory
output, which contains a JSON file.
Load the contents of the JSON file in any JSON viewer, e.g. jsonviewer.
The JSON files shows allocations segmented by stacktrace, sorted by largest first.
The heap details view contains a tree that represents the heap. The size of the root node corresponds to the selected allocator cell.
The heap can be broken down in two ways: by backtrace (marked with an ƒ), and by type (marked with a Ⓣ). When tracing is enabled, Chrome records trace events, most of which appear in the flame chart in timeline view. At every point in time these trace events form a pseudo stack, and a vertical slice through the flame chart is like a backtrace. This corresponds to the ƒ nodes in the heap details view. Hence enabling more tracing categories will give a more detailed breakdown of the heap.
The other way to break down the heap is by object type. At the moment this is only supported for PartitionAlloc.
To keep the trace log small, uninteresting information is omitted from heap dumps. The long tail of small nodes is not dumped, but grouped in an
<other> node instead. Note that although these small nodes are insignificant on their own, together they can be responsible for a significant portion of the heap. The
<other> node is large in that case.
In the trace below,
ParseAuthorStyleSheet is called at some point.
The pseudo stack of trace events corresponds to the tree of ƒ nodes below. Of the 23.5 MiB of memory allocated with PartitionAlloc, 1.9 MiB was allocated inside
ParseAuthorStyleSheet, either directly, or at a deeper level (like
ParseAuthorStyleSheet, we can see which types were allocated there. Of the 1.9 MiB, 371 KiB was spent on
ImmutableStylePropertySets, and 238 KiB was spent on
It is also possible to break down by type first, and then by backtrace. Below we see that of the 23.5 MiB allocated with PartitionAlloc, 1 MiB is spent on
Nodes, and about half of the memory spent on nodes was allocated in