At adagger, we’ve encountered and resolved several instances of memory leaks, most notably during a migration from Vue2 to Vue3, where a memory leak surfaced while using Server-Side Rendering (SSR). In this post, we’ll explain what memory leaks are, how to identify and debug them, and how to prevent them in the future.
A memory leak occurs when an application unintentionally holds onto memory that it no longer needs. This memory is not freed up for reuse, leading to inefficient resource usage. Over time, the unclaimed memory accumulates, potentially causing the application to slow down or even crash.
In JavaScript-based applications, especially in single-page applications (SPAs) and server-side rendering (SSR) setups, memory leaks can occur if objects, event listeners, or other resources are not properly cleaned up when they are no longer needed.
Detecting a memory leak can be challenging, but some common symptoms include:
In our most recent case, when we were approached by a client to investigate a memory leak that surfaced after the client's frontend was migrated from vue2 to vue3, there were multiple 503 status codes logged in the cloud run instance of the web frontend with the following error message:
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
When looking at the container memory utilisation chart a jump in memory allocation became obvious after the deployment of the migrated version.
Fortunately, several tools exist to help developers identify and track down memory leaks. Some of the most common include:
--inspect
flag: For server-side or SSR applications, Node.js has built-in memory profiling tools that can be accessed by running Node with the --inspect
flag.At adagger, we encountered an interesting memory leak issue after a web frontend was migrated from Vue2 to Vue3 with Server-Side Rendering (SSR) using nuxt. Here’s how we debugged and resolved it.
Since the memory leak occurred in the production environment, ideally the production build is used to debug the memory consumption. There might be subtle differences between dev and prod builts which might distract when debugging a memory leak. Hence try to run the production build locally. The frontend that was affected was built on top of nuxt3 using the vuetify UI library. The build was created via:
npm run build
Using the --inspect
flag in Node.js, we connected to the SSR server via Chrome DevTools.
node --inspect .output/server/index.mjs
This allowed us to take heap snapshots and compare them over time. In these snapshots, we noticed that components and certain instances of data were being retained even after they were no longer in use.
When using the application, the memory usage might either stay constant over time (ideal) or increase significantly over time. In our case it was sufficient to refresh the pages via F5 to create requests which blocked additional memory on every request. However, you might also consider using a software to automatically send requests against your application, similar to a load test.
We then iteratively removed component from the page, re-built the application and again sent requests. Typically at some point memory usage stabilises and then you know which component is the culprit. Another option to identify the problematic component is to take a heap snapshot before sending requests and then again another one after sending multiple requests. Chrome dev tools allow you to drill down the diff in memory allocation and by looking at the objects in memory you might be able to identify which element causes the memory leak.
Chances are you are not the only one affected by a specific memory issue. It might help to check relevant github repos for memory-leak related issues and potential fixes.
While our Vue migration experience highlighted how memory leaks can crop up in unexpected ways, there are some general best practices that can help you avoid these issues in your projects:
Memory leaks, if left undetected, can lead to significant performance issues in any application, whether on the frontend or server-side. At adagger, we’ve tackled memory leaks across a range of projects, from frontend frameworks like Vue.js to large-scale backend systems. By using proper monitoring tools, thoroughly profiling your code, and following best practices for resource cleanup, you can ensure your applications remain efficient and performant.
If you’re dealing with memory leaks or facing other performance challenges, we’d be happy to help. Feel free to reach out to our team of experts for a consultation and let’s make your application run as smoothly as possible!
Need help with your next project?
Contact us for more information on how we can assist you with software development, performance optimization, and more.
By sharing this real-world example of how we fixed a memory leak during a Vue.js migration, we hope to provide insight into common pitfalls and best practices, while demonstrating the value of a thorough approach to debugging.