The findrefs command extends llnode to allow you to discover which objects refer to another object and enhances the memory analysis capabilities of llnode. llnode is a plugin that allows you to explore Node.js core dumps with the lldb debugger.

Basic usage:

v8 findrefs <object_address>

This will scan the heap to find all references to <object_address> and prints out the referring objects address, it’s name and the name of the property that contains the reference.

You can already use v8 findjsobjects to identify which kinds of objects are occupying the most space on your heap. Then you can use v8 findjsinstances to list instances of those objects and v8 inspect to see what they are and understand what they are storing in the space they consume.

This helps you understand what is consuming memory. However in garbage collected languages what you usually need to know is “Why hasn’t this object been garbage collected yet?”. This is where the findrefs command comes in. Garbage collectors remove objects when no other objects refer to them. The garbage collector inside v8 does that by scanning all the objects on the heap and marking objects that are referred to by others as live. This means following references from one object to another, starting at a set of roots, and marking every one it reaches as “live”. Any that aren’t reached during this walk are free to be garbage collected (gc’d) so the space they occupy can be re-used.

The key point is that if you are wondering why you have so many of one type of object or why one very large object is occupying memory you need to know what is referring to those objects. “v8 findrefs” helps answer the question “What’s keeping these alive?”.

For a simple example we can look at the version field in the process object. Every node process should have a process object and the version field just tells us what version of node was running.

(lldb) v8 findjsinstances process
0x00001911f7408469:<Object: process>
(lldb) v8 inspect 0x00001911f7408469
0x00001911f7408469:<Object: process properties {
...
    .version=0x00001911f74258a9:<String: "v4.4.7">,
...

From this we know that the process object, 0x00001911f7408469, refers to a string, 0x00001911f74258a9 via it’s “version” property. If we run v8 findrefs against that string we should see the original reference from the properties object:

(lldb) v8 findrefs 0x00001911f74258a9
0x1911f7408469: process.version=0x1911f74258a9
0x3693cc0bb369: (String).<Second>=0x1911f74258a9

We do but we also see another reference from the field (String).<Second> in 0x3693cc0bb369. Which begs the question, what is that? Part of the answer is that not all v8 strings are the same. When v8 concatenates strings, instead of creating a new String object containing the result of joining both Strings it may create a concatenated string object which just refers to both sub-strings. This saves space over having all three strings (the two that were joined and the result) in memory. There’s actually no way to access this at a JavaScript level but findrefs will show it to you so you can see what’s keeping the String alive.

At this point it’s probably worth including a snippet of code from my test program (see this post https://developer.ibm.com/node/2016/08/25/nodeinfo-command-for-llnode/) which just dumps the process object:

...
console.log("process.version: " + process.version)
...

That line creates a concatenated string to pass to console.log which explains the extra reference to the string from process.version. Running v8 inspect (with -F to show the full string) on 0x3693cc0bb369 confirms this:

(lldb) v8 inspect -F 0x3693cc0bb369
0x00003693cc0bb369:<String: "process.version: v4.4.7">

This string object probably won’t live long, it should go out of scope as soon as console.log() returns and then be eligible for GC the next time it runs. The original string “v4.4.7” is referred to from the global process object and will survive for the lifetime of the script.

In a larger program that’s leaking memory you can’t investigate every instance of every object. If you pick a few examples of the type of object that’s occupying the most space or has the most instances by running v8 findjsobjects then v8 findjsinstances you’ll probably find a representative object quite quickly, if many objects are being created for the same reason then it’s likely that picking a few at random will give you a representative sample quite quickly.

1 comment on"findrefs command for llnode."

  1. […] dumps using the llnode plugin for lldb and describes new commands available in the LLNODE plugin findrefs command for llnode and nodeinfo command for llnode. He also provided a key enhancement to the underlying LLDB debugger […]

Join The Discussion

Your email address will not be published. Required fields are marked *