- Services
- Industries
- Technologies
- How We Work
- Blog
- Careers
- Company
- Contact Us
- <desc><defs><g fill="none" fill-rule="evenodd" id="Page-1" stroke="none" stroke-width="1"><g fill="#929292" id="icon-111-search"><path d="M19.4271164,20.4271164 C18.0372495,21.4174803 16.3366522,22 14.5,22 C9.80557939,22 6,18.1944206 6,13.5 C6,8.80557939 9.80557939,5 14.5,5 C19.1944206,5 23,8.80557939 23,13.5 C23,15.8472103 22.0486052,17.9722103 20.5104077,19.5104077 L26.5077736,25.5077736 C26.782828,25.782828 26.7761424,26.2238576 26.5,26.5 C26.2219324,26.7780676 25.7796227,26.7796227 25.5077736,26.5077736 L19.4271164,20.4271164 L19.4271164,20.4271164 Z M14.5,21 C18.6421358,21 22,17.6421358 22,13.5 C22,9.35786417 18.6421358,6 14.5,6 C10.3578642,6 7,9.35786417 7,13.5 C7,17.6421358 10.3578642,21 14.5,21 L14.5,21 Z" id="search"></path></g></g></defs></desc>
Hunting memory leaks in JavaScript using Chrome DevTools
Have you ever left a web app opened in a tab for a couple of hours to come back and see this screen? In this post we will learn how to use the Chrome DevTools to find memory leaks.
Memory management
JavaScript is a garbage collected language, but we should still learn about memory management, as making memory leaks in your code is quite easy. In short the garbage collector tracks memory allocation and when a piece of allocated memory is not needed any longer, it frees it. Unreachable pieces of memory are considered garbage, but for the rest only the developer can decide whether it’s needed or not. So, the main causes of memory leaks in JavaScript are unwanted references like accidental global variables, forgotten intervals, detached DOM elements and others.
Visualize using Timeline
Can you spot a problem with the following code snippet?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | classTask{ ... render(){ this.element= document.createElement('li'); ... this.deleteButton=document.createElement('button'); this.deleteButton. title =this.destroy.bind(this); this.element.append(this.deleteButton); return this.element; } destroy(){ this.element.remove(); } } class List{ ... add(title){ consttask=newTask(title); this.element.append(task.render()); this.tasks.push(task); } } |
Don’t worry if you can’t figure it out, the issue is not so obvious. To diagnose if an application has a memory leak, you can use the Performance section in Chrome DevTools. In the above example we want to check if continuous usage of the application (adding/removing tasks) causes unexpected increase of memory usage. To test this we can record the memory usage while adding and removing a task several times. We expect that when we add a task the memory usage increases and when we remove it – the memory usage drops back to the previous level.
- Start a recording
- Add a task
- Remove the newly added task
- Click on “Collect garbage”
- Go back to 2.
- When you perform this a few times stop the recording and look for patterns that grow as shown in the screenshot:
As seen on the screenshot above the nodes and listeners progressively increase and never drop while the
expected outcome is a graph similar to the JS Heap line above.
Note that garbage collection is non-deterministic. In other words, it is not possible to be certain when a collection will be performed, so Chrome DevTools gives us an option to force it (step 4).
Discover detached DOM using Heap Snapshots
When it comes to finding where the problem originates the Memory section in Chrome DevTools comes in handy. With a couple of
snapshots we can find which parts of the application use more memory than expected and whether we have any detached DOM elements:
- Reload the page.
- Take a heap snapshot
- Add a few tasks and remove them
- Take another heap snapshot
- Compare the two snapshots and you’ll see the second one uses more memory although we have removed all tasks.
- Expand the “Detached DOM tree” list and you will see there are still references to the DOM elements that were removed (marked in red).
Turns out our Task’s destroy method has a bug – we call the DOM removemethod, but we still keep a reference to the DOM element, so it’s never collected. When we get that fixed and perform the same test in the Performance tab, we’ll see the pattern we want:
The overall memory usage is proportional to the number of tasks we have and removing tasks actually drops
the memory usage.
Conclusion:
Memory leaks are common in JavaScript. Some of them may cause serious performance issues, so it’s a good idea to get familiar with memory management and start profiling at an early stage, especially when working on big applications. In this article we’ve only scratched the surface, but it’s still a good start.
Resources
Here you can get the fully working examples:
- memory leak – codepen and gist
- memory leak fixed – codepen and gist
Home » Hunting memory leaks in JavaScript using Chrome DevTools