find the previous version of this document at crankshaft/memory-profiling.md
Table of Contents generated with DocToc
- Theory
- Tools
- Considerations to make code easier to debug
- Resources
- memory held by object itself
- arrays and strings may have significant shallow size
- memory that is freed once object itself is deleted due to it becoming unreachable from GC roots
- held by object implicitly
- made up of handles that are created when making a reference from native code to a JS object ouside of V8
- found in heap snapshot under GC roots > Handle scope and GC roots > Global handles
- internal GC roots are window global object and DOM tree
- primitives are leafs or terminating nodes
- strings stored in VM heap or externally (accessible via wrapper object)
- VM heap is heap dedicated to JS objects and managed byt V8 gargabe collector
- native objects stored outside of VM heap, not managed by V8 garbage collector and are accessed via JS wrapper object
- cons string object created by concatenating strings, consists of pairs of strings that are only joined as needed
- arrays objects with numeric keys, used to store large amount of data, i.e. hashtables (key-value-pair sets) are backed by arrays
- map object describing object kind and layout
- native objects group is made up from objects holding mutual references to each other
- not represented in JS heap -> have zero size
- wrapper objects created instead, each holding reference to corresponding native object
- object group holds wrapper objects creating a cycle
- GC releases object groups whose wrapper objects aren't referenced, but holding on to single wrapper will hold whole group of associated wrappers
- shown at the bottom inside heap snapshots UI
- nodes/objects labelled by name of constructor function used to build them
- edges labelled using property names
- retaining path is any path from GC roots to an object, if such a path doesn't exist the object is unreachable and subject to being garbage collected
- can be seen in Dominators view
- tree structure in which each object has one dominator
- if dominator is deleted the dominated node is no longer reachable from GC root
- node d dominates a node n if every path from the start node to n must go through d
read Understanding the Unicorn
- logical errors in JS that keep references to objects that aren't needed anymore
- number one error: event listeners that haven't been cleaned up correctly
- this causes an object to be considered live by the GC and thus prevents it from being reclaimed
watch | read (slightly out of date especially WRT naming, but does show the allocation timeline profiler) | read
- preferred over snapshot comparisons to track down memory leaks
- blue bars show memory allocations
- grey bars show memory deallocations
- Allocation view (selectable in top left) shows allocations grouped by function and whose traces can be followed to see the function code responsible for the allocation
- run app via
node --inspect
ornode --inspect-brk
- open DevTools anywhere and click on the Node.js icon in the upper left corner to open a dedicated Node.js DevTools instance
- select the Memory tab and there select Record allocation timeline and then click Start
- if you launched with
--inspect-brk
go back to the source panel to start debugging and then return to Memory tab
- if you launched with
- stop profiling via the red circle on the upper left and examine the timeline and related snapshots
- notice that the dropdown on the upper left has an Allocations option which allows you to inspect allocations by function
- helps identify functions responsible for allocating memory
- in case of memory leaks or performance issues due to lots of allocatd objects it can be used to track down which functions allocate most memory
- run app via
node --inspect
ornode --inspect-brk
- open DevTools anywhere and click on the Node.js icon in the upper left corner to open a dedicated Node.js DevTools instance
- select the Memory tab and there select Record allocation profile and then click Start
- the application will continue running automatically if it was paused, i.e. due to use of
--inspect-brk
- the application will continue running automatically if it was paused, i.e. due to use of
- stop profiling via the red circle on the upper left and select Chart from the dropdown on the left
- you will see a function execution stack with the functions that allocated the most memory or had children that executed lots of memory being the widest
- the sampling-heap-profiler package allows to trigger and stop heap samples programatically and write them to a file
- supposed to be lightweight enough for in-production use on servers
- generated snapshots can be saved offline, and be opened in DevTools later
read out of date graphics, but most still works as shown read example is about DOM nodes, but techniques apply in general
- taking heap snapshots is quite easy, but the challenge is understanding whats in them
- when investigating leaks it is a good idea to trigger garbage collection right before taking a snapshot via the collect garbage trashcan in the upper left
- run app via
node --inspect
- open DevTools anywhere and click on the Node.js icon in the upper left corner to open a dedicated Node.js DevTools instance
- select the Memory tab and there select Take Heap Snapshot and then click Take Sanpshot
- perform this multiple times in order to to detect leaks as explained in advanced comparison technique
Even though views here are explained in conjunction with taking a heap snapshot, most of them are also available when using any of the other techniques like allocation profile or allocation timeline.
Properties and values are colored according to their types.
- a:property regular propertye, i.e.
foo.bar
- 0:element numeric index property, i.e.
arr[0]
- a:context var variable in function context, accessible by name from inside function closure
- a:system prop added by JS VM and not accessible from JS code, i.e. V8 internal objects
- yellow objects are referenced by JS
- red objects are detached nodes which are referenced by yellow background object
- shows top level entries, a row per constructor
- columns for distance of the object to the GC root, number of object instances, shallow size and retained size.
@
character is objects’ unique ID, allowing you to compare heap snapshots on per-object basis
- to the right of the View selector you can limit the objects by class name i.e. the name of the constructor function
- to the right of the class filter you can choose which objects to include in your summary (defaults to all)
- select objects allocated between heapdump 1 and heapdump 2 to identify objects that are still around in heapdump 3 but shouldn't be
- another way to archieve similar results is by comparing two heapdumps (see below)
- compares multiple snapshots to each other
- note that the preferred way to investigate leaks is the advanced comparison technique using the summary view
- shows diff of both and delta in ref counts and freed and newly allocated memory
- used to find leaked objects
- after starting and completing (or canceling) the action, no garbage related to that action should be left
- note that garbage is collected each time a snapshot is taken, therefore remaining items are still referenced
- Take bottom line snapshot
- Perform operation that might cause a leak
- Perform reverse operation and/or ensure that action
2
is complete and therefore all objects needed to perform it should no longer be needed - Take second snapshot
- Compare both snapshots
- select a Snapshot, then Comparison on the left and another Snapshot to compare it to on the right
- the Size Delta will tell you how much memory couldn't be collected
- birds eye view of apps object structure
- low level, allows peeking inside function closures and look at VM internal objects
- used to determine what keeps objects from being collected
- GC roots actual GC roots used by garbage collector
- DOMWindow objects (not present when profiling Node.js apps)
- Native objects (not present when profiling Node.js apps)
Additional entry points only present when profiling a Node.js app:
- 1:: global object
- 2:: global object
- [4] Buffer reference to Node.js Buffers
- only available once Settings/General/Profiler/Show advanced heap snapshot properties is checked and browser refreshed afterwards
- shows dominators tree of heap
- similar to containment view but lacks property names since dominator may not have direct reference to all objects it dominates
- useful to identify memory accumulation points
- also used to ensure that objects are well contained instead of hanging around due to GC not working properly
- always shown at bottom of the UI
- displays retaining tree of currently selected object
- retaining tree has references going outward, i.e. inner item references outer item
- (global property) intermediate object between global object and an object refereced by it
- (roots) root entries in retaining view are entities that reference the selected object
- (closure) count of references to a group of objects through function closures
- (array, string, number, regexp) list of object types with properties which reference an Array, String, Number or regular expression
- (compiled code) SharedFunctionInfos have no context and standibetween functions that do have context
- (system) references to builtin functions mainly
Map
(TODO: confirm and more details)
- source of unintentional memory retention
- V8 will not clean up any memory of a closure untiil all members of the closure have gone out of scope
- therefore they should be used sparingly to avoid unnecessary semantic garbage
Use at least three snapshots and compare those.
- Take bottom line snapshot Checkpoint 1
- Perform operation that might cause a leak
- Take snapshot Checkpoint 2
- Perform same operation as in 2.
- Take snapshot Checkpoint 3
- all memory needed to perform action the first time should have been collected by now
- any objects allocated between Checkpoint 1 and Checkpoint 2 should be no longer present in Checkpoint 3
- select Snapshot 3 and from the dropdown on the right select Objects allocated between Snapshot 1 and 2
- ideally you see no Objects that are created by your application (ignore memory that is unrelated to your action, i.e. (compiled code))
- if you see any Objects that shouldn't be there but are in doubt create a 4th snapshot and select Objects allocated between Snapshot 1 and 2 as shown in the picture below
read One small step for Chrome, one giant heap for V8
- V8's ability to dynamically increase its heap limit allows taking heap snapshot when close to running out of memory
set_max_old_space_size
is exposed to V8 embedders as part of the ResourceConstraints API to allow them to increase the heap limit- DevTools added feature to pause application when close to running out of memory
- pauses application and increases heap limit which allows taking a snapshot, inspect the heap, evaluate expressions, etc.
- developer can then clean up items that are taking up memory
- application can be resumed
- you can try it by running
node --inspect examples/memory-hog
in this repo, and opening a Node.js dedicated DevTools to see it it pause due to potential out of memory crash
The usefulness of the information presented in the views depends on how you authored your code. Here are a few rules to make your code more debuggable.
Anonymous functions, i.e. function() { ... }
show as (anonymous)
and thus are hard to find
in your code. V8 + DevTools are getting smarter about this, i.e. for arrow functions where
setTimeout(() => { ... }, TIMEOUT)
will show as setTimeout
and you can navigate to the
function during a live profiling session.
However it is recommended to name your functions to make memory and performance profiling as well as debugging your applications easier.
Keep in mind that most of these are someehwat out of date albeit still useful.
- chrome-docs memory profiling
- chrome-docs Memory Analysis 101 overlaps with chrome-docs memory profiling
- chrome-docs heap profiling overlaps with chrome-docs memory profiling
- Chasing Leaks With The Chrome DevTools Heap Profiler Views
- heap profiler in chrome dev tools
- performance-optimisation-with-timeline-profiles time line data cannot be pulled out of a Node.js app currently, therfore skipping this for now
- timeline and heap profiler
- chromium blog
- Easing JavaScript Memory Profiling In Chrome DevTools
- Effectively Managing Memory at Gmail scale
- javascript memory management masterclass
- fixing memory leaks in drupal's editor
- writing fast memory efficient javascript
- imgur avoiding a memory leak situation in JS