Home > Enterprise >  NodeJS is using a lot of heap memory when nothing is happening
NodeJS is using a lot of heap memory when nothing is happening

Time:12-17

I have the following code saved to test.js:

setInterval(logMemoryUsage, 5000);

function logMemoryUsage() {
  const formatMemoryUsage = (data) => `${Math.round((data / 1024 / 1024) * 100) / 100} MB`;
  const memoryData = process.memoryUsage();
  console.log({
    rss: `${formatMemoryUsage(memoryData.rss)}`,
    heapTotal: `${formatMemoryUsage(memoryData.heapTotal)}`,
    heapUsed: `${formatMemoryUsage(memoryData.heapUsed)}`,
    external: `${formatMemoryUsage(memoryData.external)}`,
  });
}

I've run this code on two seperate machines (one of which was a fresh Ubuntu install), and on both the heapUsed is averaging around 5MB, despite my actual program not using that much:

{
  rss: '24.37 MB',
  heapTotal: '5.25 MB',
  heapUsed: '4.63 MB',
  external: '0.32 MB'
}

I understand that JavaScript is a JIT & garbage-collected language, and that node has other stuff it's doing behind the scenes, but 5 MB? It seems like an awful lot.

For reference, I encountered this on v16.18.1 and v18.12.1.

What is causing this, and is there anything I can do to reduce the amount of memory used by my node app?

Thanks.

CodePudding user response:

(V8 developer here.)

5 MB? It seems like an awful lot.

Indeed, it is. Welcome to JavaScript. This memory is used for language-level built-in objects (like Math, String, Date), as well as environment-specific globals (e.g. document in the browser, fs, net, vm in Node).

Run this script to get an idea of just how much stuff there is by default:

let g_discovered = new Set();
function Discover(obj, path) {
  g_discovered.add(obj);
  let properties = Object.getOwnPropertyNames(obj);
  for (let p of properties) {
    try {
      let v = obj[p];
      if (v === null) continue;
      if (g_discovered.has(v)) continue;
      let nested_path = path.length > 0 ? path   "."   p : p;
      console.log(`Some memory is used by ${nested_path}`);
      if (typeof v === "object" || typeof v === "function") {
        Discover(v, nested_path);
      }
    } catch (e) {
      // Ignore failures. Examples that will throw:
      // Function.prototype.arguments
      // Symbol.prototype.description
    }
  }
}
Discover(this, "");

You could create and inspect a heap snapshot if you wanted to dig deeper.

JavaScript is a JIT & garbage-collected language

JavaScript as a language doesn't have an opinion on whether it is interpreted, AOT-compiled, or JIT-compiled. Most modern engines have decided that a combination of interpreting and JIT-compiling is the way to go, but that's an implementation detail. If anything, this reduces the amount of memory that's used after initialization.

The fact that JS is garbage-collected doesn't influence the minimum memory consumption of an empty heap. It can, however, mean that e.g. the strings created by the previous run of logMemoryUsage haven't been freed yet (because why spend time on GC when less than 5MB are in use?).

just so I'm set moving forward, are there any specific things I can do to catch memory leaks and keep general memory usage low?

That's a whole separate question. You can start at https://nodejs.org/en/docs/guides/diagnostics/memory/, and come back to StackOverflow when you have a specific problem that you can describe in detail, and where you can list steps you've already tried.

  • Related