Skip to content

Latest commit

 

History

History
426 lines (314 loc) · 21.1 KB

memory-profiling.md

File metadata and controls

426 lines (314 loc) · 21.1 KB

Memory Profiling

find the previous version of this document at crankshaft/memory-profiling.md

Table of Contents generated with DocToc

Theory

Objects

read read

Shallow size

read

  • memory held by object itself
  • arrays and strings may have significant shallow size

Retained size

read

  • memory that is freed once object itself is deleted due to it becoming unreachable from GC roots
  • held by object implicitly
GC roots
  • 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

Storage

read | read

  • 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

Object Groups

read

  • 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

Retainers

read | read

  • 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

Dominators

read | read | read

  • 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

Causes for Leaks

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

Tools

DevTools Allocation Timeline

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 Stack

slides watch

allocation-stack

  • 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

Recording Allocation Timeline with Node.js

  • run app via node --inspect or node --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
  • 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

memory-hog-allocation-timeline

DevTools Allocation Profile

read

  • 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

Recording Allocation Profile with Node.js Manually

  • run app via node --inspect or node --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
  • 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

memory-hog-allocation-profile

Recording Allocation Profile with Node.js Programatically

  • 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

DevTools Heap Snapshots

memory-heapsnapshot

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

Taking Heap Snapshot with Node.js

  • 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

Views

overview | overview

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.

Color Coding

read

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
Summary View

read

  • 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
Limiting included Objects
  • 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)
Comparison View

read

  • 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
  1. Take bottom line snapshot
  2. Perform operation that might cause a leak
  3. Perform reverse operation and/or ensure that action 2 is complete and therefore all objects needed to perform it should no longer be needed
  4. Take second snapshot
  5. 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

compare-snapshots

Containment View

read

  • 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
Entry Points
  • 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
Dominators View

read

  • 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
Retainer View
  • 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

Constructors listed in Views

read

  • (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)
Closures

read

  • 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
Advanced Comparison Technique

slides

Use at least three snapshots and compare those.

  1. Take bottom line snapshot Checkpoint 1
  2. Perform operation that might cause a leak
  3. Take snapshot Checkpoint 2
  4. Perform same operation as in 2.
  5. 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

4-snapshots

Dynamic Heap Limit and Large HeapSnapshots

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
    1. pauses application and increases heap limit which allows taking a snapshot, inspect the heap, evaluate expressions, etc.
    2. developer can then clean up items that are taking up memory
    3. 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

Considerations to make code easier to debug

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.

Resources

blogs/tutorials

Keep in mind that most of these are someehwat out of date albeit still useful.

videos

slides