Garbage collection (GC) is a fundamental aspect of memory management in Node.js applications. However, inefficient garbage collection can lead to performance issues, causing application slowdowns and potentially impacting user experience. To ensure optimal performance and diagnose memory problems, it’s essential to study garbage collection traces. In this blog post, we’ll explore various methods for capturing garbage collection traces from Node.js applications.

Options to capture Garbage Collection traces from Node.js applications
There are 3 options to capture Garbage Collection traces from the Node.js applications:

‘–trace-gc’ flag
v8 module
Performance Hook
Let’s discuss them in this post.

1. ‘–trace-gc’ flag
The easiest and most straightforward approach is to pass the ‘–trace-gc’ flag along with your usual invocation command. Example:

node --trace-gc my-script.mjs

Once the ‘–trace-gc’ flag is enabled, your Node.js application will start generating garbage collection traces in the console output. These traces provide valuable insights into memory usage, GC events, and potential performance bottlenecks. Garbage Collection traces would look something like this:

[721159:0x61f0210]  1201125 ms: Scavenge 27.7 (28.8) -> 26.8 (29.8) MB, 0.5 / 0.2 ms  (average mu = 
0.999, current mu = 0.970) allocation failure 
[721166:0x5889210]  1201338 ms: Scavenge 30.7 (32.1) -> 29.7 (33.1) MB, 0.6 / 0.3 ms  (average mu = 
0.998, current mu = 0.972) allocation failure 
[721173:0x54fc210]  1202608 ms: Scavenge 26.8 (28.3) -> 25.8 (29.3) MB, 0.7 / 0.4 ms  (average mu = 
0.999, current mu = 0.972) allocation failure 
[721152:0x54ca210]  1202879 ms: Scavenge 30.5 (31.8) -> 29.6 (32.8) MB, 0.6 / 0.2 ms  (average mu = 
0.999, current mu = 0.978) allocation failure 
[721166:0x5889210]  1202925 ms: Scavenge 30.6 (32.1) -> 29.7 (33.1) MB, 0.7 / 0.3 ms  (average mu = 
0.998, current mu = 0.972) task 
[721159:0x61f0210]  1203105 ms: Scavenge 27.7 (28.8) -> 26.7 (29.8) MB, 0.4 / 0.2 ms  (average mu = 
0.999, current mu = 0.970) allocation failure 
[721173:0x54fc210]  1204660 ms: Scavenge 26.8 (28.3) -> 25.8 (29.3) MB, 0.5 / 0.2 ms  (average mu = 
0.999, current mu = 0.972) allocation failure

Tool to analyze GC Traces: Analyzing garbage collection (GC) traces manually can be a daunting task due to the wealth of information they contain. To simplify this process and gain valuable insights, consider using the GCeasy online tool. Upon uploading GC Traces to the GCeasy tool, it generates insightful graphs, metrics, and recommendations to optimize the GC performance of your Node.js application. Here is a sample GC trace analysis report generated by the tool.

2. v8 module
If you don’t want to enable GC traces for the entire lifetime of the application or if you want to enable them only on certain conditions or in the certain parts of code, then you can use the ‘v8’ module as it provides options to add/remove flags at run-time. Using the ‘v8’ module, you can pass the ‘–trace-gc’ flag and remove it as shown in the below code snippet:

import v8 from 'v8';
 
// enable trace-gc
v8.setFlagsFromString('--trace-gc');
 
// app code
// ..
// ..
 
// disable trace-gc
v8.setFlagsFromString('--notrace-gc');

3. Performance Hook
Node.js has a built-in ‘perf_hooks’ module that facilitates you to capture performance metrics from the application. You can use the ‘perf_hooks’ module to capture Garbage Collection traces. Refer to the code snippet below:

const { performance, PerformanceObserver } = require('perf_hooks');
 
// Step 1: Create a PerformanceObserver to monitor GC events
const obs = new PerformanceObserver((list) => {
  const entries = list.getEntries();
  for (const entry of entries) {
 
    // Printing GC events in the console log
    console.log(entry);
  }
});
 
// Step 2: Subscribe to GC events
obs.observe({ entryTypes: ['gc'], buffered: true });
 
 
// Step 3: Stop subscription
obs.disconnect();

If you notice in above code, we are doing the following:

We are importing the ‘performance’ and ‘PerformanceObserver’ classes from the ‘perf_hooks’ module.
We create a ‘PerformanceObserver’ instance to monitor garbage collection events (‘gc’ entry type).
And whenever Garbage Collection events occur in the application, we are logging in to the console using the ‘console.log(entry)’ statement.
We start observing GC events with ‘obs.observe()’.
Finally, we stop observing GC events with ‘obs.disconnect()’.
When above code snippet is added to your application, in the console you will start to see the GC events reported in the JSON format as below:

{
  kind: 'mark_sweep_compact',
  startTime: 864.659982532,
  duration: 7.824,
  entryType: 'gc',
  name: 'GC Event'
}
{
  kind: 'scavenge',
  startTime: 874.589382193,
  duration: 3.245,
  entryType: 'gc',
  name: 'GC Event'
}

Conclusion
In this post, we explored three main methods for capturing garbage collection traces in Node.js applications: using the –trace-gc flag, leveraging the v8 module for dynamic tracing, and utilizing the perf_hooks module. Each method offers its own advantages and flexibility in capturing and analyzing GC events. Additionally, we discussed the importance of analyzing GC traces effectively and recommended using online tools like GCeasy for simplifying the analysis process and deriving actionable insights. Hope you found it helpful.