Nodejs

[Node.js] - How to check and prevent nodejs(v8) server memoryleak

파커초 2024. 1. 6. 18:10

What are memory leaks

A memory leak is a condition that occurs when a program doesn't release the memory it allocates, i.e., step 3 of the lifecycle is not carried out. For instance, the system assigns memory locations to store values for the variables that we declare inside our program. In programming languages such as C/C++, we can allocate additional memory to hold the data and variables required. But, it's our responsibility to deallocate the memory after usage.

Memory Lifecycle

  1. Allocation of memory for the defined variable
  2. Manipulation operations such as read, write on the allocated memory
  3. After usage, releasing the allocated memory

Why Do Memory Leaks Happen in Node.js

Accidental Global Variables

Since the root node has reference to the global variables in JavaScript (i.e., global this or window), they are never garbage collected throughout the entire lifecycle of the application

let globalVal = 'Hello I`m global variable'
function foo() {
  return globalVal;
}
---
function foo() {
  globalVal = 'Hello I`m accidental global variable'
  return globalVal
}

Closures

The process of accessing the parent function's variables inside an inner function is called closure.

function parentFunction() {
  let a = parentVal
  
  return function innerFunction(innerVal) {
    return a;
  }
}

the garbage collector will not reclaim the memory for the variable even though the parent function completes the execution

Timers

  • setTimeout
    • call after some delay
  • setInterval
    • call after some delay repeatly
let obj = {
  runTimer: function() {
    let ref = this;
    setTimeout(()=> {
      console.log('Timer call');
      // runTimer function has setTimeout timer with an object reference
      // So, every time it executes the callback, it gets re-initialized
      ref.runTimer();
    }, 5000)
  }
}

obj.runTimer();
obj = null

console.log(obj)

Event Listeners

Javascript uses event listeners to handle events in DOM elements

Upstream Code

  • memory leak can be due to upstream or remote code
  • when you cannot determine the exact cause of the memory leak, checking the performance of its dependencies

V8 Garbage Collector

https://v8.dev/blog/trash-talk

The Garbage Collector (GC) traces the object reference from the root and marks all the nodes that are reachable from the root.

Algorithm

  • Reference Counting
    • check if an object has any reference to it
      • if there are none marked garbage collectible
  • Mark and Sweep
    • check object is reachable from the root node in the memory
    • more efficient than the reference counting

Minor GC

Scavenge

 
The scavenger evacuates live objects to a fresh page.
The scavenger evacuates ‘intermediate’ objects to the old generation, and ‘nursery’ objects to a fresh page.

Major GC (Full Mark Compact)

 

Marking

  • white: the initial state, this object has not yet been discovered
  • gray: object has been discovered
  • black: object and all of its neighbors discovered
 
  • using reachability garbage collector check as a proxy for ‘liveness’
  • gc follows every pointer recursively which reachable in the runtime

Sweeping

Remove all unused (white) objects.

  • check gaps in memory left by dead objects and added to data structure called a free-list
  • find contiguous gaps left by unreachable objects
  • free-list are separated by the size of the memory chunk for quick look up

Compaction (Defragmenting)

Moves all marked — and thus alive — objects to the beginning of the memory region.

  • based on a fragmentation heuristic choose to evacuate/compact some pages
  • copy surviving objects into other pages that are not currently being compacted
  • weakness of a garbage collector
    • allocate a lot of long-living objects, pay a high cost to copy these objects
    • to prevent this compact only some highly fragment pages
 

Essential tasks

  1. Identify live/dead objects
  2. Recycle/reuse the memory occupied by dead objects
  3. Compact/defragment memory (optional)

How to Detect Memory Leaks

https://nodejs.org/en/docs/guides/diagnostics/memory/#my-process-runs-out-of-memory

https://scoutapm.com/

Scout APM

Scout APM is a monitoring tool that can trace resource usage and memory bloat. Getting started with Scout is as simple as installing a package.

const scout = require("@scout_apm/scout-apm");
scout.install({
allowShutdown: true, // allow shutting down spawned scout-agent processes from this program
monitor: true, // enable monitoring
name: "", // Name comes here
key: "" // Key comes here
  });
  
const requests = new Map();
app.get("/", (req, res) => {

    requests.set(req.id, req);
    res.status(200).send("Hello World");
});
 

node-heapdump

Heapdump package dumps v8 heap for later inspection. It takes a memory snapshot using heapdump and helps to profile it for performance bottleneck and memory leak.

var heapdump = require("heapdump");

heapdump.writeSnapshot(function(err, filename){
    console.log("Sample dump written to", filename);
});

node-inspector, chrome devTools …

How to fix the memory leak

you will realize fixing the memory leak is somewhat easier than diagnosing it.

Fixing Accidental Global variables

function foo() {
// javascript hoists it as a global variable
  bar = 'This is global'
}
  • use ‘use strict’

Use Global variables Effectively

  • use less global variables

Use Closures Effectively

var newElem;

function parent() {
    var someText = new Array(1000000);
    var elem = newElem;
 

    function child() {
        if (elem) return someText;
    }

    return function () {};
}

setInterval(function () {
    newElem = parent();
}, 5);
  • Although elem is never used by parent function, it references ‘elem’

Debugging

  • Determining how much space our specific type of objects
  • What variables are preventing from being garbage collected

Can We Avoid Memory Leaks to Begin With?

  • it is difficult to avoid it entirely at the beginning itself
  • it is best to have ample observability of your application`s performance all the time

refer: https://blog.meteor.com/an-interesting-kind-of-javascript-memory-leak-8b47d2e7f156

Reference