Automatically Detect Memory Leaks With Puppeteer
About half a 12 months in the past Bronley Plumb kindly made me conscious of a memory leak in one among my open-supply packages. To see this memory leak in action it was essential to open a browser and its dev tools to execute some manual steps. On high of that, the memory needed to be inspected manually. It was a complicated process. Usually I just add a failing check earlier than I fix a bug. This time it was a bit extra difficult. But in the long run I discovered a means to check the memory consumption robotically and Memory Wave here is what I came up with. If you are not interested in the adventurous path which led me to the solution be happy to skip proper to the top to read on from there. What is a memory leak? Basically a memory leak is the state of affairs wherein a software program holds on to a piece of memory which it doesn't really want anymore. In JavaScript this most certainly means that there's a reference to an object someplace which you totally forgot about.
However for Memory Wave the rubbish assortment it is unattainable to distinguish between objects which are still in use and those which have just been forgotten somewhere. Historically a memory leak was something that internet builders did not need to care about. Every link on a web page induced a new page to be loaded which in turn wiped the memory. However memory leaks are normally very shy and solely develop into noticeable when a specific program keeps working for a very long time. With todays Single Web page Functions and Progressive Web Apps the scenario has modified. Many websites do behave like apps and are designed to run for a long time and that is particularly true for apps that use the web Audio API. The memory leak in question was present in standardized-audio-context which is a library to achieve cross browser compatibility for that API. Essentially the most simple example of a memory leak that I may think of is attaching some metadata to an object.
For example you have a few objects and you need to store some metadata for each of them. But you do not want to set a property on those objects since you need to maintain the metadata in a separate place. This may be solved by utilizing a Map as proven in the next snippet. It allows to retailer some metadata, to get it again and to delete it once more. All that is needed is a Map which uses an object as the key to index its metadata. However what if an object with metadata is not referenced anyplace else anymore? It still cannot be garbage collected as a result of the Map nonetheless has a reference to it to index the metadata. The next instance is of course contrived however many memory leaks will be decreased to one thing as simple as this. All of the created objects do survive every garbage collection because the Map still has a reference to them. This is the right use case for a WeakMap.
The references held by a WeakMap do not prevent any object from being rubbish collected. By replacing the Map with a WeakMap this frequent trigger for a memory leak might be eliminated. The issue that induced the memory leak in my code was very related although it was not that obvious to spot. Puppeteer is a tool which can be used to distant control Chrome or some other Chromium browser. It's a simpler alternative to Selenium and WebDriver but it surely has the draw back that it only works with browsers based mostly on Chromium (for now). It comes with access to some APIs which are not accessible by Selenium because it tries to work together with a web site like an actual user. Puppeteer however has access to many APIs which aren't accessible to regular users. This works by utilizing the Chrome DevTools Protocol. A kind of things that Puppeteer can do which Selenium can't is inspecting the memory. And MemoryWave Community this is after all super useful when trying to find memory leaks.
At first glance there seems to be a operate within the API of Puppeteer which provides all that is required to trace the memory utilization. It's the web page.metrics() methodology. It does among other things also return a metric known as JSHeapUsedSize. That is the number of bytes that V8, MemoryWave Community the JavaScript engine utilized in Chrome, uses as memory. Sadly getting the scale of the memory will not be sufficient. The memory of a JavaScript program is managed by a very autonomous rubbish assortment. Unlike the garbage assortment in the true world which often exhibits up on a really strict and well-known schedule the JavaScript garbage collection does its job every time it thinks it's the best time to do so. It will probably normally not be triggered from throughout the JavaScript code. But it surely is critical to verify it ran earlier than inspecting the memory to make certain that all the trash has been picked up and the memory consumption has been computed based mostly on the latest adjustments made by the code.