JavaScript Memory leaks

JavaScript Memory Leaks and How To Fix Them

Detect and fix memory leaks to boost your application performance – and what to do to avoid them in the future.

Memory leaks in JavaScript are pretty common. In the “old” days, when we had an application that would refresh the page on almost every click, we did not have any issue with that. Today, when we have Single Page Applications (SPA) and long running web applications, managing your memory correctly and avoiding leaks is a must for your app performance.

Main Takeaways

  • Memory leaks are created when we keep unintentional references to objects and thus prevent Garbage Collection from clearing the objects
  • Using chrome developer tools it is very easy to detect a memory leak.
  • Once you detected a leak, you can fix it
  • You can fix a memory leak by making sure you have no unintentional reference to objects you do not need anymore.

What is a Memory Leak?

A memory leak in JavaScript is defined as an increase in application memory consumption over time because allocated objects are not released.

In “old” languages like C and C++ the developer had to allocate and release the memory explicitly. If the program would have lost a reference to an object, that would have resulted in a leak since the developer would not have been able to clear the object.

In JavaScript, on the other hand, only objects that have no reference from the code are cleared. Garbage collection (GC) is the process that’s responsible to clear these unreferenced objects from memory. 

Hence, in JavaScript, most leaks are defined as unintentional references to objects which prevent the GC from clearing them – even though they are unused.

The symptoms that hint on a leak is an application that gradually declines in performance over time. It would usually happen in a SPA that starts smoothly and after a few user’s actions starts to run slower and slower. In Node.js it will be seen as a server that responds slower and slower to api calls.

Memory Leak Detection in JavaScript

Let’s see how to find JavaScript memory leaks using a few simple profiling tools. The first basic tool in frontend is the performance tab. When profiling an application, we can see the memory level at the time of profiling. If the level is rising without any decline, that’s one hint. If we initiate a garbage collection and the level does not go back to baseline, that’s an even bigger hint.

Memory leak detection in JavaScript - Memory profile
Figure 1: The blue area above the timeline signifies memory level as a function of time. It increases rapidly and even when a GC is initiated (yellow arrow), the level does not go down to baseline.

In figure 1 we see a memory profile of an application running. The memory (blue area above the timeline) keeps rising. The yellow arrow marks a GC point which does not reduce the memory to the baseline level (the level at the beginning of the recording).

This might hint on a leak – but this is not necessarily a leak. It might be that our application received data from the server or that it created DOM elements for showing some data. You will have to understand the scenario in which you have profiled in order to make sure the leak hint is indeed hinting on a leak and not a valid memory allocation.

The performance tab is not the only JavaScript memory leak tool. Moreover, memory profiling is not the main usage of this tool. It is very useful to raise your suspicion regarding a leak in a certain use-case in your application.

The Memory tab in the dev tools is the tool that can give you detailed allocation information. With this information, you can pinpoint your heaviest allocating functions or find the exact objects or arrays that keep reference to objects you might have wanted to be garbage collected.

The “classic” way of finding leaks was using the Heap snapshot in the Memory tab. This snapshot gives you the current state of memory at the time the snapshot was taken. When using Heap snapshot, you’d usually: 

  1. Get to a starting point in the application
  2. Take a snapshot of the baseline state 
  3. Enact some use case in the application
  4. Take another snapshot
  5. Revert the use case and return to the starting point
  6. Take another snapshot
  7. Compare the first snapshot to the last one.

So for instance, you start your app, click a button that opens a modal window and close the modal window. You’d expect that the comparison of the first and last snapshot will yield minor differences. That means that in our case, opening and closing the modal removed most of the objects that were allocated in the process of opening and interacting with the modal.

DOM elements - How to detect a memory leak in JavaScript
Figure 2: Snapshot view

In figure 2 we see 3 snapshots were taken (blue rectangle). We compare snapshot 3 to snapshot 1 (using the dropdowns in the green rectangle). The red rectangle shows us a major difference between snapshot 3 and 1. 3000 DOM elements were added and were not cleared. 

Might it be that our modal window created elements that are still referenced from the code?

Then again, it might not be the case, because you’d might expect the modal to have some side effects – like getting data from the server and populating a data grid. It might be the user filled a form and the data saved prompted the creation of new objects. In any event, you will see the objects that were created and decide if it is valid they are still there or if you expect them to be garbage collected.

An easier way to find memory leaks and their origin is to use the Allocation instrumentation on timeline. In this tool, you see the live allocation of the objects as they are created. You also see the live garbage collection of the objects.

Recording a timeline - Finding memory leak in JavaScript
Figure 3: Memory allocation timeline recording.

Figure 3 shows the recording on a timeline. Figure 3a shows the recording of 3 allocations that were not cleared. We can see that the bars are still blue (hence, they are still in memory). We can also see the allocation in each blue bar is at about 51.2kb. 

Figure 3b shows a focus on one of the allocations. We can see what was allocated (a 1000 DOM elements in this case) and we can find out the retainers of the DOM elements. The fact we have “Detached” before the DOM elements is a big hint for a DOM leak. This means we have DOM elements that were removed, but are still referenced from our code.

Figure 3c shows the same scenario only now the elements were garbage collected and appear as gray bars. Notice the allocation level is 1kb which means even the blue bar is something very small and negligible and we have nothing to fear in regards to a leak in this case.

The third option of finding a source for a leak is the allocation sampling. This tool gives you the data about how much memory was allocated by certain functions.

Memory allocation in JavaScript sampling
Figure 4: Allocation sampling results.

Figure 4 shows the result of allocation sampling. Figure 4a shows the bottom up view. We can see that addElement was the function that allocated the most memory in the app. Figure 4b shows a chart of the functions in the app by their allocation size.

With these JavaScript profiling tools you can easily find leaks. You can find the allocating function or the allocated leaked objects and handle them in your application.

How To Fix A Memory Leak?

A JavaScript memory leak fix can be applied once you detect its source. 

After all we’ve read so far, avoiding leaks is pretty easy. Make sure you do not have a reference to objects that you wish to be cleared. We mentioned an example of leaked DOM elements. They were removed from the DOM (detached) but not from our JavaScript code (referenced from an object or array).

There are other types of memory leaks. Another common type is forgotten timers or listeners. For instance, you’ve setup a setInterval at a certain point in the application and then closed that part of the application. The setInterval, being a global function, keeps on running until you run clearInterval. If you forgot to use clearInterval or clearTimeout it will result in the interval and all of its context are still referenced and not garbage collected.

You’d might also add listeners to a DOM element, and then forget to remove them when they are no longer needed. For instance, a modal window that sets a listener on some external element. The modal is closed, but the external element still has that listener in place. This will cause the same effect as with forgotten timers, as the listeners are kept on the element itself.

Summary

Memory leaks in JavaScript usually happen in complex applications. The garbage collection algorithm is improving and makes it harder to create leaks. As shown in the examples in this article, they can still happen.

Leaks happen when your app still references objects that are no longer needed. In the frontend it might be DOM elements or any other object. In the backend it might be pieces of data received from an external source. We also saw that timers and event listeners can be the source of a leak.

The main symptom for a leak is an application that slows over time. We learned how to check and fix memory leaks in your JavaScript application. Profiling using the methods shown in this article can help you to quickly identify if there’s a leak and the source of the leak.


Once in a week I publish a FREE newsletter for developers and programmers who are interested in taking their skills to an expert level.

And I invite you to sign up right now. Just a few of my optimization techniques can make a big difference to the performance of your applications.

Sign Up for our FREE newsletter and get our best articles delivered to you by e-mail


What you will learn:

  • How to make simple changes to your code to achieve the HIGHEST JavaScript performance possible
  • An easy, effective way to TEST your code and find places to make improvements
  • Practical tips on how to improve JavaScript MEMORY performance and reduce garbage collection
  • An expert advice on JavaScript from the leading programmers in the field
  • JavaScript Performance hacks and shortcuts to save you time and boost efficiency

Enter your name and email to access your FREE membership

*** We take your privacy very seriously. We hate spam as much as you do, so rest assured that your contact details will never be shared with a third party.


JavaScript Performance Optimization Tips
Node.JS performance optimization

Easy ways to test and improve Node.js performance

Here’s how to measure Node.js code efficiency and significantly boost Node’s performance. Simple tips that will improve your Nodejs code CPU and memory consumption.

Javascript JS performance optimization

Test and optimize JavaScript performance

Practical steps to help you find and eliminate JS performance problems. A step-by-step guide to fixing common JavaScript performance bottlenecks in Web Apps.


JS garbage collection

How to optimize JavaScript garbage collection

Find out how garbage collection works in JavaScript and what exactly you need to fix in order to achieve the highest performance of your application.