Fuite – a tool for finding memory leaks in SPA

Debugging memory leaks in web applications is tricky. Tools exist, but they are complex, cumbersome, and often do not provide an answer to a simple question: Why is my application leaking memory?

Because of this, I bet most web developers don’t actively monitor memory leaks. And of course, if you’re not testing something, bugs can easily slip through.

When I first started looking into memory leaks, I thought they were rare. How can JavaScript – a language with an automatic garbage collector – become a big source of memory leaks? But the more I learned, the more I suspected that memory leaks are actually quite common in single page applications (SPA) – it’s just that nobody checks it out!

Since most web developers don’t mess with chrome memory tools for fun, they probably won’t notice the leaks until the browser tab crashes with an Out Of Memory error, or the page slows down, or someone accidentally opens. Open Task Manager and notice that the website uses up a lot of megabytes (or even gigabytes!) Of memory. But at that moment, things got so bad that there could be several leaks on one page.

I already wrote about memory leaks in the past, but my advice basically boils down to this: “Use the Chrome DevTools, follow this dozen tedious steps, and then, maybe, you can figure out why your page is leaking. ” This is not the best experience for developers and I’m sure many readers shook their heads in desperation and continued. It would be much better if the tool could automatically detect memory leaks.

This is why I wrote fuite (in French “leak”). fuiteis a command line tool that can be pointed to any url and it will analyze the page for memory leaks:

npx fuite <https://example.com>

That’s all! By default, it assumes the site is a client SPA and will crawl the page for internal links (such as /aboutor /contact). Then the following steps are performed for each link:

  1. Click on the link

  2. Click the browser back button

  3. Repeat to see if the memory grows

At fuiteWhen detecting leaks, it will show you which objects are believed to be causing the leak:

For this fuiteuses the basic strategy outlined in my blog post. This will launch Chrome, run some script P number of times (7 by default) and see if any leaked objects are a multiple of P times (7, 14, 21, etc.).

fuitewill also parse any arrays, objects, maps, sets, event listeners, and the general DOM to see if any of them are leaking. For example, if the array grows by exactly 7 after 7 iterations, it is likely leaking.

Testing Real Websites

Somewhat surprisingly, a “basic” scenario of following internal links and hitting the back button is sufficient to detect memory leaks in many SPA. I tested fuite on the home pages of 10 popular front-end frameworks and found leaks in all of them:

In this case, “internal links” refers to the number of internal links tested, “average height” refers to the average memory growth for each link (ie, clicking on it and then pressing the back button), and “maximum height” refers to to the internal link that leaked the most. Note that these numbers do not include a one-time setup cost as fuite does one preflight iteration before the usual 7 iterations.

To confirm these results yourself, you can use tab Chrome DevTools Memory … Here’s a screenshot of the worst site in my set, where I click a link, hit the back button, take a heap snapshot and repeat:

On this particular site, the memory grows by about 6MB each time you click a link and return.

To avoid names and shame, I have not listed the actual websites. The point is to show a representative sample of some popular SPA – the authors of these websites can independently run fuiteand track these leaks. (Please do it!)

Caveats

Note, however, that not every SPA leak is a major issue that needs to be addressed. SPAs must, for example, maintain focus and scroll state to properly maintain accessibility, which means there might be some small metadata stored for every page navigation. fuitewill dutifully report such leaks (because they are leaks), but the developer must decide whether to pursue the tiny leak or not.

Some memory growth can also be attributed to internal browser changes (such as JITing) that the web page cannot control. Thus, memory growth numbers are an imperfect measure of what you can get from fixing leaks – it may well be that a few kilobytes are inevitable. (Though fuitetries to ignore the browser’s internal growth and will only say “leaks detected” if there is actionable advice for the web developer.)

On rare occasions, some memory growth can also be attributed to obvious browser bugs. Analyzing the sites above, I did find one (Site # 4) that seems to be suffering from this Chrome error because of <img loading="lazy">not unloaded. Unfortunately, fuiteBrowser errors will be hard to spot, so if you are confused about a leak, cross-validation with other browsers is advised!

Also note that a Multiple Page Application (MPA) leak is nearly impossible because the browser cleans up memory on every page navigation. (Of course, provided there is no errors in the browser .) During testing, I found two frontend environments whose home pages were MPA and, unsurprisingly, fuitecould not find any leaks in them. They were excluded from the results above.

Memory leaks are more of a concern for SPA, where memory is not automatically cleaned up on every navigation. fuiteprimarily intended for SPA, although you can run it on MPA as well.

fuitecurrently only the JavaScript heap memory in the main page frame is measured, so cross-origin frames, web workers, and service workers are not measured. Something like [performance.measureUserAgentSpecificMemory()](<https://chromestatus.com/feature/5685965186138112>)would be more accurate, but this is only available in isolated contexts with cross origin so it is impractical for a multi-tool right now.

Other memory leak scenarios

The “crawl internal links” script is just the default script – you can also create your own. fuitebuilt on the basis Puppeteer so for any scenario you want to test, you just need to write a Puppeteer script to tell the browser what to do. Here are some common scenarios you can test:

  • Open a modal dialog and then close it

  • Hover over an item to display a tooltip, then hover over to close it

  • Scroll through the endless loading list, then go back and forth

  • ETC.

In each of these scenarios, you expect that the memory will be the same before and after. But of course, web applications are not always that simple! You might be wondering how many of your dialogs and tooltips are hiding memory leaks.

For leak analysis fuitecaptures heap snapshot files which you can download to Chrome DevTools for testing. It also has --debuga mode that can be used for more detailed analysis: stepping through a test while it is running, debugging the browser in real time, analyzing object leaks, etc.

Under the hood fuiteit is a fairly simple tool and I will not argue that it can do 100% of the job of fixing memory leaks. There is still a human component – figuring out why your objects were selected and saved and then finding a smart solution. But my goal is to automate ~ 95% of the work, so this actually becomes achievable fix memory leaks in web applications.

I express my deep gratitude to my wife for her help in preparing the translation.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *