Importance of tracking memory allocation in node js

Saranraj

Saran

July 08, 2022 Author

As applications have become more complex, Node.js developers have had to advance. It has become more vital to understand how the runtime works internally in order to avoid difficulties in production. The importance given to optimizing the application so that it only consumes the resources is very high in the market today, as consumers demand only for the best. Having this knowledge might save app owners a lot of money in the long run, and also enable them to have a greater footing in the market.

A process called memory allocation is usually responsible for all the memory related issues in the development process, and we will concentrate on this element in this article. We will be taking a look at it within the context of node.js and will take a technical look at how it works so that developers using the language can understand it.

How does memory allocation work in node.js?

We will first understand how node.js and google's javascript engine, the V8, work together in managing the memory required for the application. In node.js, memory is divided into 3 segments. Together, they form what is called the resident set.

How does memory allocation work in node.js

  • Heap: heap is the part where memory allocation happens for all applications. All objects are stored here.
  • Stack: stack is the memory that is used by the local variables of the application
  • Code segment: code segment stores the raw string

When an application starts, it triggers the following chain of processes.

  • Memory Heap is created based on the app's requirements
  • Application occupies the heap
  • The garbage collector(also called GC) cleans up the heap

Memory heap divisions

The memory heap is further divided into several spaces, but for the focus of the article, we will focus on the following main division.

  • Old space: Objects are usually transferred here after a period of time in a new space. All old objects are stored in this space. The flag -max-old-space-size has power over the old space.
  • New space: It's small and designed to be cleaned often with the help of the garbage collector. Most objects are usually stored in this part of the heap.

While the new space's allocation is relatively better for app performance, the new space also has a size between 1 and 8MB, which is quite small. As a result, clearing the items as soon as feasible is a good idea in this area of the heap to free up memory for new objects and avoid them being allocated in the old place. The new space is further divided into the "from" space & "to" space. The division is made to better handle the operations of the garbage collector.

From the JavaScript side, Node.js provides an API for controlling the garbage collector, so that developers can track what's going on in it. Only the objects that require a strong reference are passed to the old space, since doing such an operation is usually expensive. It's worth noting that when an object from old space is accessed through "to" space, your CPU's cache locality is lost, which may harm speed because the application isn't leveraging CPU caches. All of these processes mentioned happen in seconds, but it is quite complicated when we peek into it.

Collecting data back from the old space

Behind the scenes, the garbage collector(GC) manages several threads, one of which is to flag memory blocks for freeing. This means that in any Node.js application, a thread is monitoring the old space for memory addresses that aren't reachable, indicating that they can be freed. This method is also known as mark-and-sweep. It works as follows

  • The GC marks all the memory addresses that can't be reached leaving the ones that can be reached. This is called the mark phase.
  • The GC then executes the sweep phase. At this phase, the marked memory addresses that are marked are freed.
  • The last phase is called the compact phase, where V8 moves objects inside the heap. This phase is considered expensive.

Memory allocation that is not regulated correctly harms the application

Rather than collecting from old space, V8 likes to allocate more heap memory. So, just because memory utilization never decreases in an app, it doesn't necessarily imply a memory leak. However It's more effective to allocate tiny short-lived objects regularly in Node.js (or especially V8) than to change huge long-lived ones. This is due to the inner workings of the garbage collector/GC, which was discussed in the previous section.

The V8 waste collection system is now extremely efficient. However, when an application allocates and frees large blocks of memory, the event loop may still risk becoming stuck. Javascript Engines have worked hard to make the garbage collector as efficient as possible. Due to user mistakes in previous Node.js versions, it was prone to causing bottlenecks in the application.

Tools for investigating memory allocation issues

Clinic doctor

In most cases, it's more effective to monitor the Event Loop metric than tracing everything the garbage collector does. Clinic Doctor is a useful tool to accomplish something like this. It aids in the diagnosis of performance issues in your application and directs you to more specialized tools for further investigation. This tool may be used to evaluate symptoms such as poor CPU consumption, halting trash collection, frequent event loop delays, or a chaotic amount of active handles.

Node.js memory snapshot

Another useful tool for keeping track of memory use is Memory Snapshot. All work on the main thread, however, stops when producing a snapshot. It might take a minute or more, depending on the contents of the heap. To produce a heap snapshot, you'll need around twice as much memory as the heap at the moment. As a result, there's a chance the procedure will fail due to an Out of Memory error (out-of-memory). There are many ways in which one can generate a memory snapshot.

  • Via inspector protocol
  • Using the command line
  • With an API called writeHeapSnapshot
  • With Chrome Dev Tools

Clinic heap profiler

The Clinic.js set of tools includes the Heap Profiler. Its goal is to use Flamegraphs to discover memory allocation by functions. The flamegraph is a graphical representation of memory allocation over time. A function's memory allocation is represented by each block. The bigger the block, the more memory it got.

Conclusion

As one can imagine, memory allocation is an important topic for any programming Language, and node.js is no different. It is important for programmers to understand at least the basics of memory allocation and keep that in mind to design high performing server side applications. It is also helpful to identify and debug issues in an app that has suspicious or high memory usage. If you have an app idea that is complicated and need an app developed in a way that contains no memory related bugs, it is better to approach a good node js development company. Companies like Nextbrain create high performing apps in node.js for its clients since they are well versed in this domain.