Search code examples
javascriptnode.jsout-of-memoryv8pm2

Run A Function Right Before Nodejs Crashes Due to OOM


Is it possible to force a Node.js app to run a function (i.e. send an API call) right before it crashes due to OOM?

I am trying to log the timestamps of OOM crashes, but this requires the ability to run a function right before the process gets killed. PM2 is currently being used to manage this Node.js process.


Solution

  • Medium: Detect heap overflow on Node.js: JavaScript heap out of memory

    Uses the cluster module. Forks a worker that on exit event forks another worker that logs:

    total_heap_size: The size V8 has allocated for the heap. This can grow if used_heap_size needs more. heap_size_limit: The absolute size limit the heap cannot exceed. e.g.: max_old_space_size

    It then exits and spawns a new worker and runs the main function again. This prevents crashing on out of memory, since it exits before the out of memory error is triggered and spawns a new worker. If you still want it to crash you can remove process.exit().

    heavyHeapConsumer is a demo function that consumes a large amount of memory.

    const cluster = require('cluster');
    const v8 = require('v8');
    
    let heavyHeapConsumer = () => {
      let arrays = [];
      setInterval(() => {
        arrays.push(new Array(1000000));
      }, 100);
    };
    
    if (cluster.isMaster) {
      cluster.fork();
      cluster.on('exit', (deadWorker, code, signal) => {
        // Restart the worker
        let worker = cluster.fork();
        
        // Note the process IDs
        let newPID = worker.process.pid;
        let oldPID = deadWorker.process.pid;
        
        // Log the event
        console.log('worker ' + oldPID + ' died.');
        console.log('worker ' + newPID + ' born.');
      });
    } else { // worker
      const initialStats = v8.getHeapStatistics();
      
      const totalHeapSizeThreshold = 
        initialStats.heap_size_limit * 85 / 100;
      console.log("totalHeapSizeThreshold: " + totalHeapSizeThreshold);
      
      let detectHeapOverflow = () => {
        let stats = v8.getHeapStatistics();
        
        console.log("total_heap_size: " + (stats.total_heap_size));
        
        if ((stats.total_heap_size) > totalHeapSizeThreshold) {
          process.exit();
        }
      };
      setInterval(detectHeapOverflow, 1000);
      
      // here goes the main logic
      heavyHeapConsumer();
    }