Garbage Collection

Introduction

  • “Reachable” values are those that are accessible or usable somehow. They are guaranteed to be stored in memory.

  • Garbage collection is performed automatically. We cannot force or prevent it.

  • Objects are retained in memory while they are reachable.

  • Being referenced is not the same as being reachable (from a root): a pack of interlinked objects can become unreachable as a whole, as we’ve seen in the example below

Simple Example

let user = {
  name: "John"
};

user = null;
  • The object is created

  • However after set to null, there is no reference on the object, so the object can be collected and free the memory

// user has a reference to the object
let user = {
  name: "John"
};

let admin = user;

user = null;
  • The object cannot be collected as it is referenced by admin unless we set admin to null too

Interlinked objects

function marry(man, woman) {
  woman.husband = man;
  man.wife = woman;

  return {
    father: man,
    mother: woman
  }
}

let family = marry({
  name: "John"
}, {
  name: "Ann"
});

  • In order to delete the man , we need to remove family.father and family.mother.husband

  • if we set family to null then, both woman and man can be collected

GC Trace

node --trace-gc script.mjs
  • It should output the following

[13973:0x110008000]       44 ms: Scavenge 2.4 (3.2) -> 2.0 (4.2) MB, 0.5 / 0.0 ms  (average mu = 1.000, current mu = 1.000) allocation failure
Token value
Interpretation

13973

PID of the running process

0x110008000

Isolate (JS heap instance)

44 ms

The time since the process started in ms

Scavenge

Type / Phase of GC

2.4

Heap used before GC in MB

(3.2)

Total heap before GC in MB

2.0

Heap used after GC in MB

(4.2)

Total heap after GC in MB

0.5 / 0.0 ms (average mu = 1.000, current mu = 1.000)

Time spent in GC in ms

allocation failure

Reason for GC

Types of GC

Introduction

  • Initially, objects are in the Nursery region.

  • If they survive the first round of GC, they will be moved to the Intermediate region

  • If they survive the second round of GC, they will be moved to the Old Generation region.

  • For each generation, the algorithm is different:

    • Young generation — Minor GC (Scavenger)

    • Old generation — Major GC (Full Mark-Compact)

Mark-sweep

  1. Mark: mark the roots as “reachable”

  2. Visit and recursively mark objects directly or indirectly referenced by roots as “reachable”

  3. Sweep: unmarked ones are removed

  4. Compaction: To reuse the small and scattered memory gaps left behind by dead objects, we would process the “compact” task: copy surviving objects into other pages and free the old page.

Scavenger

  • Divided the young generation into two parts, we only have to limit the moving behavior within the page. Instead of focusing on the dead objects, we only focus on the surviving ones. We move all surviving objects to a contiguous chunk of memory within the page, and thus the from-space has become the “whole garbage.” Free the “From-Space” all!

  • After moving, To-Space becomes From-Space and vice-versa. And the From-Space becomes empty, and new allocations can be done in the From-Space.

  • Space for the young generation is limited to 16MB, and it would quickly reach its limit. Objects that survive a second GC are evacuated into the old generation rather than To-Space.

Clean GC Manually

  • Only work when expose-gc flag is available

  • Here is an example

global.gc();
console.log(process.memoryUsage());
let vm = new WeakMap();
let key = new Array(1000000);
vm.set(key,1);
global.gc();
console.log(process.memoryUsage());
key = null;
global.gc();
console.log(process.memoryUsage());
node --expose-gc index.js

Reference

Last updated

Was this helpful?