Investigating Memory Leaks
and performance tuning your Javascript
@abinavseelan
blog.campvanilla.com
What's in store
- What exactly is memory? 🙃
- Garbage Collection 🚚
- What is a memory leak? 🤔
- Common causes. 💀
- Investigating memory leaks! 🔍
Memory?
Allocate the memory
Use the allocated memory
Release the memory
// Allocation
let a = 10; // allocates memory to hold the value 10
const obj = { // Allocates memory for obj and extends that memory for each key
firstName: 'foo',
lastName: 'bar'
};
const arr = [1, 2, 3]; // Allocates memory for arr
const now = new Date(); // Allocation via function call
// Use
a = 20; // Modify the allocated memory
obj.birthday = new Date(); // Extend the allocated memory
Allocate the memory
Use the allocated memory
Release the memory
Garbage Collector
The
A garbage collector is a mechanism that helps reclaim unused memory
Reference Counts helps indicate when memory is unused
function someFunc() {
}
function someFunc() {
const someConst = 10;
// While inside the function someFunc,
// someConst has a reference to it
// hence the memory allocated to it
// will not be reclaimed
}
// Outside someFunc, someConst is not referenced
// and the Garbage collector cleans it up
const someConst = 10;
const obj = {
val: someConst // the constant someConst has a reference count of 1
}
const newObj = obj; // the object obj has a reference count of 1
newObj.newVal = someConst; // someConst has a reference count of 2
obj.val = 10;
// Here, someConst loses one reference count
// someConst has a reference count of 1
newObj = {
foo: 'bar'
}
// Here, both someConst and obj lose one reference.
// obj has a reference count of 0
// someConst has a reference count of 0
// Since both have a reference count of 0, they will be garbage collected!
However...
// Example from MDN
function someFunc() {
const obj = {};
const newObj = {};
// Create a cyclic reference
obj.val = newObj;
newObj.val = obj;
}
// Even outside the function someFunc
// obj and newObj have 1 reference count
// and the garbage collector will not
// clean this up.
Reference Counts Unreachability helps indicate when memory is unused
Mark & Sweep Algorithm
Unreachable Code
Unreachable Code
Memory Leaks
A memory leak occurs when you don't need an object, but the runtime thinks you do and you're unintentionally using memory
But...why should you care?
A pretty common symptom of a memory leak is that a page's performance gets progressively worse over time.
Jank
Common Causes
- Accidental Global Variables
- Forgotten Timers
- Closures
- Detached DOM trees
- Retaining tree
Common Causes
- Accidental Global Variables
- Forgotten Timers
- Closures
- Detached DOM trees
- Retaining tree
function someFunc() {
b = 10; //looks pretty harmless right?
}
function someFunc() {
b = 10;
}
// is the same as
function someFunc() {
window.b = 10; // Oops!
// This happens due to scope lookup
// If the variable does not exist in any scope
// the JS runtime creates the variable
// in the GLOBAL scope! 😱
}
// The variable is never cleaned
Common Causes
- Accidental Global Variables
- Forgotten Timers
- Closures
- Detached DOM trees
- Retaining tree
setInterval(() => {
const someConst = 10;
// do something here
}, 1000);
// someConst may be cleaned at some point
// The handler itself is still referenced
// and will not be cleaned unless ...
// The fix? Assign the reference to a variable
let interval = setInterval(() => {
const someConst = 10;
// do something here
}, 1000);
// some code
// When you're done with the interval
clearInterval(interval);
Common Causes
- Accidental Global Variables
- Forgotten Timers
- Closures
- Detached DOM trees
- Retaining tree
// A trivial example
function someFunc() {
const a = 10; // 4 bytes, let's say
return function() {
console.log(a);
}
}
const arrayOfFunction = [];
for (let i = 0; i < 1000000, i++) {
arrayOfFunction[i] = someFunc();
}
// A real-world example
const inputField = document.getElementById('input-field');
function someFunc() {
// do something with inputField
}
// inputField is not longer referenced anywhere
// it can be cleaned.
// A real-world example
const inputField = document.getElementById('input-field');
const button = document.getElementById('cta');
button.addEventListener('click', () => {
// do something with inputField
});
// Here, it will not be cleaned since the runtime
// cannot decide when the memory allocated to
// inputField is not longer required
The runtime is smart though
function someFunc() {
const a = 10;
const b = 20;
this.sendAlert = function() {
alert(a);
}
}
const btn = document.getElementById('cta');
btn.addEventListener('click', () => {
new someFunc().sendAlert();
// Only the memory allocated for `a` will not be cleaned.
});
Common Causes
- Accidental Global Variables
- Forgotten Timers
- Closures
- Detached DOM trees 💀
- Retaining tree
A detached DOM tree is a DOM tree that is not part of the actual DOM tree, but is referenced by Javascript
Javascript
DOM
Javascript
DOM
DOM Node
Javascript
DOM
DOM Node
Reference
Javascript
DOM
DOM Node
Reference
let inputContainer = document.getElementById('input-field-1');
let button = document.getElementById('cta');
let inputBox = inputContainer.querySelect('input');
button.addEventListener('click', () => {
inputContainer.remove();
// removes the DOM node from the DOM tree
});
// But, it is still referenced in JS by inputBox
// This is a detached DOM tree.
Common Causes
- Accidental Global Variables
- Forgotten Timers
- Closures
- Detached DOM trees
- Retaining Tree
const obj = {
numVal: 100, // 4 bytes
strVal: 'Hello World' // 11 bytes
}
const btn = document.getElementById('cta');
btn.addEventListener('click', () => {
alert(obj.numVal);
// This will hold on to 15 bytes
// not 4 bytes
});
Chrome Dev Tools
Investigating leaks using
performance.memory
Chrome's
Task Manager
The
Memory Tab
The
Demo
https://goo.gl/2AzHX9
Build first, optimise after
A recap
Memory goes through a memory life-cycle
Garbage collectors use a mark-and-sweep algorithm to reclaim memory
Memory leaks are caused when the garbage collector and you as the programmer are basically ... not in sync.
Memory leaks cannot be found by a compiler/linter. You should take extra care to avoid them. :)
A recap
The most common causes for memory leaks are
- Accidental global variables
- Forgotten Timers
- Closures
- Detached DOM trees
- Retaining Trees
A recap
To investigate a memory leak, first fire up the task manager to see the memory footprint
Use the dev tools to then narrow down the type of memory leak that is happening! :)
That's all folks! 🚀
@abinavseelan
blog.campvanilla.com
Copy of Investigating Memory Leaks (and performance tuning your Javascript)
By Vilva Athiban
Copy of Investigating Memory Leaks (and performance tuning your Javascript)
Talk given at 1) Flipkart's UI Bootcamp, 2018 2) AngularJs & ReactJS Meetup on 21st Oct, 2017
- 277