Garbage Collector Stress
Tags: node, garbagecollector, stress, performance
Published: 2020-08-30 11:53:51

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](https://medium.com/voodoo-engineering/nodejs-internals-v8-garbage-collector-a6eca82540ec) by [Vincent Vallet](https://twitter.com/vince_vallet) it is the best one.

But, here you are a practical explanation!

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

```js
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:

```sh
[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`:

```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!!**

```sh
[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!