Learn How to Debug the Cause of Memory Leak in SSR

Debugging the cause of SSR memory leak in Next.js

FlyingSquirrel
6 min readMay 5, 2022

TL; DR;

- It might be running out of memory. In that case, you need to set --max_old_space_size to increase manually the maximum heap memory.
- When started with the --inspect switch, you are able to start inspecting client with DevTool.
- You need to compare objects in which retained size is too much larger than shallow size. The objects are cause of memory leak.
It increased sharply and dropped abruptly over and over. That is not normal.

After migrating to SSR, just found out there was a memory leak.

I believe it is a really good opportunity to face lots of problems when I use some libraries or something in REAL production. I would have learned just usages when I use those libraries in my toy projects.

After migrating a project in my charge to SSR, it made a sharp increase in heap memory and the Nodejs server was dead and restarted repeatedly.

Running out of memory even if increasing heap memory size manually

It is not a sudden increase in traffic, and I tried to increase heap memory size manually, but the mountain-shape graph shows up over and over. It means probably there is a memory leak in Node.js.

You can increase the maximum size of heap memory manually with --max_old_space_sizeoption like below. In Node.js document, on a machine with 2 GB of memory, consider setting this to 1536 (1.5 GB) to leave some memory for other uses and avoid swapping.

node — max-old-space-size=1536 index.js

Start debugging with --inspect option

// package.json with npm
"script" : {
"build": "next build",
"start": "cross-env NODE_OPTIONS='--inspect' next dev",
}
# package.json with yarn berry
"script" : {
"build": "next build",
"start": "cross-env NODE_OPTIONS='$NODE_OPTIONS --inspect' next dev",
}
$ yarn build && yarn start // go to http://localhost:{PORT}

It is okay with the local development server to debug, but I tried to build the application first with next build command and open the inspector with --inspect because the local development server is tooooo slow to profile memory.

In Next.js, you can set NODE_OPTIONS option with cross-env. (👉 See Next.js Docs)

yarn build && yarn start and access both http://localhost:YOUR_PORT andchrome://inspect. Then now you can see the list available to debug.

Or it is also possible to debug with VScode, JetBrain IDE because they are supporting inspect mode.

In chrome://inspect, there is a list available to debug. Once clicking “inspect”, DevTools will be open.

Explore DevTools

In Memory tab, you can see this if refreshing the page while recording.

In DevTools Memory tab, you can choose a profile type. For me, I chose Allocation instrumentation on timeline. Click a record button and do anything you want to debug and toggle the button, you can see the snapshot after a while.

  • Heap snapshot: Recorded the current heap memory usage. It is useful when you want to compare before with after.
  • Allocation instrumentation on timeline: Like above, it shows heap memory usage on the timeline while recording.
  • Allocation sampling: It can be used when you want to profile longer periods of time by sampling.
The Grey bar means freed memory by GC, Blue bar means used memory. A long blue bar means that there is a memory leak.

The Grey bar means freed memory by GC, Blue bar means used memory while your application is running but it is not freed by GC. The long blue bar is the cause of the memory leak. 👀

Simply put SSR with Next.js just responds to every request with proper HTML, JS, and CSS using Node.js server.

Nodes that can not be reached from the root are garbages.

When my code is executed in Node.js environment, all variables are part of the huge object graph. There can be multiple root nodes(a.k.a GC roots) like Window, Global, DOM, etc. In each relation of nodes, there should be a root node. And in some cases, there can be variables that can not be reached from the root and we can call them “garbage”. Garbage collector marks and sweeps garbage and frees up memory that is no longer needed.

The memory that is not freed by GC occurs memory leak.

Node.js responds properly to every request. Heap memory is allocated to compute and respond. After responding, we hope that GC cleans up and returns memory to the system, however, some variables are still alive in some cases, occurs memory leaks. Memory has limitations, at some point, you will run out of memory.

Find out objects in which retained size is too larger than the shallow size

Selecting the longest blue bar then find out something in which retained size is too large

Finally, it is time to find out objects in which retained size is too larger than the shallow size. It is maybe not easy to find. You need to spend lots of time finding the causes of memory leaks.

Just compare Shallow size with Retained size.

  • Shallow Size: the size of memory that is held by the object itself. (self)
  • Retained Size: the size of the memory that is freed up once the object is deleted along with its’ dependent objects. (self + descendants)

To find the causes of memory leaks, it is efficient to take a look at the very large retained size for shallow size. In short, Find the small shallow size and large retained size.

Chrome Developer Documentation

Once click the object’s retained size is too large for the shallow size, you can see below named “Objects” that describe very detailed information to inspect memory usages.

There is a file path of the Object. You can guess what libraries cause memory leaks or what functions cause the memory leaks.

If you find a file path causes memory leaks, just presume and modify your code. And do profile again. This process might be able to make you bored. Just be patient and do it.

And if you also found any of problems when you are using a library, visit Gihub issue, discussion, or search results like Google or Reddit. There should be a question written by someone who has the same question like you.

After I fixed, it is all good. No more increasing-dead graph again.

SSR is going to be such a hot trend I guess. There are pros and cons when using SSR, and I think the advantages outweigh the disadvantages. The Thing you should keep in mind is you need to care more about it.

References

--

--

FlyingSquirrel
FlyingSquirrel

Written by FlyingSquirrel

감성이 말랑말랑한 개발자입니다.

No responses yet