1 Routinely Detect Memory Leaks With Puppeteer
Jewell Collick edited this page 1 month ago


About half a 12 months in the past Bronley Plumb kindly made me aware of a memory leak in certainly one of my open-supply packages. To see this memory leak in action it was necessary to open a browser and its dev instruments to execute some manual steps. On top of that, Memory Wave the memory needed to be inspected manually. It was a complicated procedure. Normally I simply add a failing test earlier than I repair a bug. This time it was a bit more difficult. But ultimately I discovered a method to test the memory consumption automatically and right here is what I came up with. If you are not interested in the adventurous path which led me to the answer be at liberty to skip right to the tip to learn on from there. What is a memory leak? In general a memory leak is the state of affairs by which a software holds on to a bit of memory which it would not really want anymore. In JavaScript this most probably means that there is a reference to an object somewhere which you completely forgot about.


But for the rubbish collection it's unimaginable to tell apart between objects that are nonetheless in use and those which have simply been forgotten somewhere. Historically a memory leak was one thing that web builders didn't must care about. Each hyperlink on a page prompted a new page to be loaded which in turn wiped the memory. But memory leaks are often very shy and only grow to be noticeable when a selected program keeps operating for a long time. With todays Single Page Purposes and Progressive Net Apps the situation has modified. Many websites do behave like apps and are designed to run for a very long time and that is especially true for MemoryWave apps that use the web Audio API. The memory leak in question was found in standardized-audio-context which is a library to realize cross browser compatibility for that API. Probably the most simple instance of a memory leak that I may think of is attaching some metadata to an object.


As an example you might have a couple of objects and also you wish to store some metadata for every of them. However you do not wish to set a property on those objects since you want to maintain the metadata in a separate place. This can be solved by using a Map as proven in the next snippet. It allows to store some metadata, to get it again and to delete it once more. All that is required is a Map which makes use of an object as the important thing to index its metadata. But what if an object with metadata shouldn't be referenced wherever else anymore? It still can't be rubbish collected because the Map still has a reference to it to index the metadata. The following example is of course contrived however many memory leaks can be diminished to one thing as simple as this. All of the created objects do survive each rubbish collection as a result of the Map nonetheless has a reference to them. That is the right use case for a WeakMap.


The references held by a WeakMap don't stop any object from being garbage collected. By changing the Map with a WeakMap this widespread trigger for Memory Wave a memory leak might be eliminated. The issue that brought about the memory leak in my code was very related though it was not that obvious to spot. Puppeteer is a instrument which can be used to remote management Chrome or every other Chromium browser. It's a less complicated various to Selenium and WebDriver however it has the downside that it solely works with browsers based mostly on Chromium (for now). It comes with entry to some APIs which aren't accessible by Selenium as a result of it tries to work together with a website like an actual user. Puppeteer however has entry to many APIs which are not accessible to normal users. This works by utilizing the Chrome DevTools Protocol. A kind of things that Puppeteer can do which Selenium cannot is inspecting the memory. And this is in fact tremendous useful when looking for MemoryWave memory leaks.


At first glance there appears to be a perform in the API of Puppeteer which offers all that is needed to trace the memory usage. It's the web page.metrics() technique. It does among other issues also return a metric called JSHeapUsedSize. This is the variety of bytes that V8, the JavaScript engine used in Chrome, uses as memory. Unfortunately getting the scale of the memory is just not sufficient. The memory of a JavaScript program is managed by a very autonomous rubbish assortment. In contrast to the garbage assortment in the real world which often reveals up on a really strict and well known schedule the JavaScript garbage assortment does its job each time it thinks it's the suitable time to do so. It will probably usually not be triggered from throughout the JavaScript code. However it is important to verify it ran earlier than inspecting the memory to make sure that all of the trash has been picked up and the memory consumption has been computed based on the most recent changes made by the code.