Memory leaks: the forgotten side of web performance
nolanlawson.com
nolanlawson.com
Example: you split a string into words for spell-checking purposes, once you've spell-checked each word you cache it so that you don't have to ask the spell-checker again if it's valid or not, now each time the original string is edited every single intermediate string produced during the edit could be kept in memory just because you are caching a word out of each of them. This can get out of hand super quickly.
Each time you are extracting a string out of another string this problem can arise.
I'm not an expert in this space, but it seems to me that the JavaScript engine could eventually clean up that memory, maybe based on some heuristics (e.g. the size of the original string, whether any references to the original string exist, etc.).
"Issue 2869: Substring of huge string retains huge string in memory"
Status: Assigned (Open)
Reported 2013
Another example of how rough this can be is any program speaking over a network because it's really easy to end up with every network packet you've ever gotten (more or less) hanging around in memory because you've got this or that tiny bit of some string that was constructed to represent some chunk of the stream.
Surprised to see it in a javascript context, though. I'd have thought they'd have long since passed the point where they found out they have to take copies.
I ended up writing a hack-string-copy function "substrForceMemCopy" that forced the runtime to allocate a new string by using substr on one less character than I actually wanted and then adding that missing character to force creation of a new string.
Main part of the function:
return s.substr(start, length - 1) + s.charAt(start + length - 1);If you suspect a leak with an array or object, one thing that helped me in the past was to create a unique element, and put that on a array or object. The tools for seeing rogue loose elements are easier to follow than the tools to detect where a leak is coming from - changes are easy to find with before and after diffs. If the element doesn’t get garbage collected, then the array or object still has a reference.
E.g.
function someFunction() {
xxyArray = [];
xxyArray.__leakdetect = document.createElement(‘LEAKDETECTxxyArray’);
someObject = {};
someObject.__leakdetect = document.createElement(‘LEAKDETECTsomeObject’);
// do stuff with xxyArray and someObject
}
Edit: Only do that when debugging, since elements are big and slow. Note that you don’t insert the element into the page DOM. For DOM “component” objects with a lifecycle (SPA app built using a component framework), I added a LEAKDETECT element into the page itself as part of the component constructor, and removed it from the page when the component destructor was called.Completely agree! I find that a lot of developers don't understand how things work under the hood or how things fit together. This always comes back to bit them. Same can be said for modern infrastructure engineers / DevOps / sysadmins.
The reasonable approach is to load test the mostly used workflows, optimise if necessary - anything else would be fixing imaginary issues, working on non-existing problems, etc - there is normally no time or money for it.
Shouldn't this be handled by the browser gc? If the element isn't in the DOM and there aren't any js references left anymore
However, if you're adding event listeners to the document, the window, or some permanent element (header, footer, etc.), then those won't be GC'ed since the DOM node is never GC'ed.
Reddit behaves similarly, with its endless scrolling pages and whatnot. Invariably during a heavy Reddit session my browser will run out of memory. On Linux this is extra infuriating as the system will become unresponsive while it tries to swap first.
This is one of the reason I dislike GC. By enabling seemingly care-free usage as well as making it hard to reason about when resources are released, it's easy to dismiss thinking about it at all.
Sure GC will save one's ass in trivial cases, but few non-trivial become way more difficult to discover.
In some cases that's totally fine! For example, if you have a stateless fleet of server-side applications and you can restart any of them at any time, or if you have a CLI that runs for short amounts of time.
In other cases, for example long-running stateful servers, games or GUI apps, it can quickly become a real problem.