Tech talk: Javascript Memory Leaks and JSWhiz
Todays tech talk revolved around the recently published JSWhiz whitepaper from google. The paper discusses common javascript memory leak patterns. It also goes over how those leaks can be created and how google automated detection of them using Closure type annotations.
JSWhiz
First Google identified common javascript memory leak patterns, though some of these are described in terms of Closure syntax
Create without dispose EventHandler - “Closure objects of type EventHandler provide a simple mechanism to group all events and listeners associated with an object together. This allows for easy removal of listeners when the events being listened to can no longer fire. Unfortunately this is also one of the main sources of leaks”
Setting member to null without removing event listeners. “In JavaScript setting a variable to null is the idiomatic way of providing a hint to the garbage collector that the memory allocated by an object can be reclaimed. But the EventHandler and event listeners attached to the object prevents the garbage collector from disposing of the object and reclaiming memory.”
Undisposed member object has EventHandler as member
Object graveyard (holding onto objects in a persistent structure like a list/dictionary so objects can’t ever get collected)
Overwriting EventHandler field in derived class. - “Closure implements inheritance using a prototype-based approach [8], which can break the expectations of programmers coming from a more standard OOP language background (such as C++ or Java). For EventHandlers this can cause memory leaks as the overwritten EventHandler cannot be freed [8].”
Unmatched listen/unlisten calls. - “The semantics of the listen and unlisten calls require all parameters to be the same. When the parameters do not match, the event listener is not removed and a reference to objects being listened remains, inhibiting the garbage disposal.”
Local EventHandler - “A local EventHandler instance that does not escape scope, e.g., a locally defined variable that is not assigned to a field member, added to an array, or captured in a closure, can not be disposed of later.”
How
Google’s engineers leveraged the fact that they had an annotated AST of javascript (with the Closure compiler) to look these particular patterns and help identify potential leaks. It’s not always as easy as it sounds though, as the paper mentions in one section
Doing the analysis in a flow-insensitive manner is equal to assuming that an eventful object is disposed of if there exists a disposal instruction in the AST tree. This assumption is further necessitated by 1) the dynamic nature of JavaScript, 2) most disposals resulting from runtime events (user actions, that may or may not occur) and 3) the computational demands associated with full program/inter-procedural analysis.
Other pitfalls of their current analysis include
Not tracking objects with fully qualified names (such as application.window.toolbar)
Objects that are never returned
Objects not captured in closures
Not checking of double disposal
Still, it’s an impressive feat.
Discussion
After running through the paper we started talking about different garbage collection issues with javascript. One of the big problems people have encountered is that since the DOM and javascript use reference counting you can easily get yourself into circular references which would prevent collection. We talked a bit about generational collection since that’s how the V8 engine that chrome and node.js use, and how that can solve circular references by doing path from root detection.
A couple other neat points came up, such as that the delete
keyword doesn’t actually delete memory. It only deletes properties of an object. Just to double check that we found ourselves at the mozilla dev site which had this hilariously appropriate quote on the page for the delete
keyword
The delete operator removes a property from an object. Unlike what common beliefs suggests, the delete operator has nothing to do with directly freeing memory (it only does indirectly via breaking references.
Conclusion
In the end we decided that these memory leak patterns aren’t strictly related to javascript. Lots of the same issues occur with actionscript and other languages (even .NET), where you have strong references in the form of event handlers or closures and things can never be garbage collected.
The ultimate way to avoid memory leaks is to be cognizant of who uses what, who holds on to what, and what needs to be removed when.