loading...
Cover image for Garbage Collector Stress

Garbage Collector Stress

eomm profile image Manuel Spigolon ・3 min read

Have you ever listen to someone says:

don't stress the garbage collector

???

But, what it means?

The theory of the Node.js Garbage Collector has been broadly described on the web. I think this article by Vincent Vallet it is the best one.

But, here you are a practical explanation!

Create a new stress.js file with this simple code:

function doSomeThing () {
  const s = `${'a'.repeat(10e6)}-${'v'.repeat(10e6)}`
  return s
}

setInterval(doSomeThing, 10)

and run it with node --trace-gc stress.js

You will see an endless output like this:

[8904:0000018D2126F130]       39 ms: Scavenge 2.1 (3.3) -> 1.7 (4.3) MB, 1.5 / 0.0 ms  (average mu = 1.000, current mu = 1.000) allocation failure 
[8904:0000018D2126F130]     1302 ms: Scavenge 2.4 (4.8) -> 2.0 (4.8) MB, 6.2 / 0.0 ms  (average mu = 1.000, current mu = 1.000) allocation failure 
[8904:0000018D2126F130]     5992 ms: Scavenge 2.9 (4.8) -> 2.0 (5.3) MB, 2.2 / 0.0 ms  (average mu = 1.000, current mu = 1.000) allocation failure 
[8904:0000018D2126F130]    10989 ms: Scavenge 3.0 (5.3) -> 2.0 (5.3) MB, 0.2 / 0.0 ms  (average mu = 1.000, current mu = 1.000) allocation failure 
[8904:0000018D2126F130]    14104 ms: Mark-sweep 2.6 (5.3) -> 1.6 (4.3) MB, 4.2 / 0.0 ms  (+ 0.1 ms in 2 steps since start of marking, biggest step 0.1 ms, walltime since start of marking 10 ms) (average mu = 1.000, current mu = 1.000) finalize incremental marking via task GC in old space requested
[8904:0000018D2126F130]    14730 ms: Mark-sweep 1.7 (4.3) -> 1.6 (4.3) MB, 0.5 / 0.0 ms  (+ 0.5 ms in 4 steps since start of marking, biggest step 0.3 ms, walltime since start of marking 23 ms) (average mu = 0.998, current mu = 0.998) finalize incremental marking via task GC in old space requested
[8904:0000018D2126F130]    20152 ms: Scavenge 2.6 (4.3) -> 1.6 (4.3) MB, 0.2 / 0.0 ms  (average mu = 0.998, current mu = 0.998) allocation failure
...

The Garbage Collect Scavenge process is run many times every ~5 seconds! Forever!!

What is happening under the hood?

  • a big string s is allocated and added to the New Space
  • the s string is released
  • a new big string s is allocated and added to the New Space
  • the s string is released
  • another new big string s is allocated and added to the New Space
  • the s string is released
  • the Heap "New Space" is full, so the Garbage Collector Scavange phase starts and clean all the s allocations
  • restart from the beginning

Now fix the script moving the s variable in the parent scope and run the changed file node --trace-gc no-stress.js:

const s = `${'a'.repeat(10e6)}-${'v'.repeat(10e6)}`
function doSomeThing () {
  return s
}

setInterval(doSomeThing, 10)

The output is completely different:

  • only 2 calls to Scavange
  • only 2 Mak-sweep

in a 15 minutes run!!

[16388:000002874922BD50]       33 ms: Scavenge 2.1 (3.3) -> 1.7 (4.3) MB, 1.2 / 0.0 ms  (average mu = 1.000, current mu = 1.000) allocation failure 
[16388:000002874922BD50]     9338 ms: Scavenge 2.4 (4.8) -> 2.0 (4.8) MB, 4.7 / 0.0 ms  (average mu = 1.000, current mu = 1.000) allocation failure 
[16388:000002874922BD50]    18877 ms: Mark-sweep 2.2 (4.8) -> 1.6 (4.8) MB, 3.6 / 0.0 ms  (+ 0.8 ms in 3 steps since start of marking, biggest step 0.7 ms, walltime since start of marking 17 ms) (average mu = 1.000, current mu = 1.000) finalize incremental marking via task GC in old space requested
[16388:000002874922BD50]    19484 ms: Mark-sweep 1.6 (4.8) -> 1.6 (5.3) MB, 1.7 / 0.0 ms  (+ 1.1 ms in 2 steps since start of marking, biggest step 1.1 ms, walltime since start of marking 3 ms) (average mu = 0.995, current mu = 0.995) finalize incremental marking via task GC in old space requested

What is happening under the hood?

  • a big string s is allocated and added to the heap space
  • the s string survive to two Scavange's generations and goes to Old Space
  • no more stress on the Garbage Collector!

This means that you may optimize your long-running Node.js process reusing variables and functions instead of generating new ones every time in your hot path code like a validation function for every HTTP request that executes a RegExp!

So, don't stress the Garbage Collector!

Discussion

pic
Editor guide