On Github jalopez / js-memory-management
Javier López Pardo - @javilp
Tip: We will use Chrome Dev Tools. They are awesome!
Which kind of app do I have?
Memory vs. Performance vs. Development Cost
"Early optimization is the root of all evil" D. Knuth// window var a = 3;
var myObject = {
prop1: a, // It's a copy
prop2: 'A string'
};
var myArray = [
myObject, // By reference
new Date()
];
myArray = null;
myObject = null;
myArray = null;
myObject.prop1 = null; // delete myObj.prop1;
myObject = null;
What's wrong with this?
function testLeak() {
myArray = [];
for (var i = 0; i < 1000000; i++) {
myArray.push(i.toString());
}
return myArray[0];
}
function runTest() {
testLeak();
}
function testLeak() {
var myArray = []; // Don't forget the var here
for (var i = 0; i < 1000000; i++) {
myArray.push(i.toString());
}
return myArray[0];
}
function runTest() {
testLeak();
}
DON'T USE GLOBAL VARIABLES!
Blue lines are allocated memory
Grey lines are freed memory
Use heapdump
var heapdump = require('heapdump');
heapdump.writeSnapshot('/tmp/step1.heapsnapshot');
executeLeakyAction();
heapdump.writeSnapshot('/tmp/step2.heapsnapshot');
Load *.heapsnapshot in Chrome Profile
Not only global, but also long-live objects:
var ToDoList = function() {
this._items = [];
this._itemIndex = 0;
this._mainNode = document.getElementById('main');
};
ToDoList.prototype.addItem = function() {
var li = document.createElement('li');
li.innerHTML = 'Item ' + this._itemIndex++;
this._items.push(li);
this._mainNode.appendChild(li);
};
ToDoList.prototype.removeAll = function() {
for (var i = 0, len = this._items.length; i < len; i++) {
this._mainNode.removeChild(this._items[i]);
}
};
Remove all references to DOM nodes
function testLeak() {
var myArray = [],
mySecondArray = [1, 2, 3, 4],
anotherObject = {a: 'b'};
for (var i = 0; i < 1000000; i++) {
myArray.push(i.toString());
}
return function leakClosure() {
return myArray;
};
}
var closure;
function createMemory() {
closure = testLeak();
}
function freeMemory() {
closure = null;
}
Closures retain scope variables
function registerEvent() {
var text = new Array(10000000).join('x')
var node = document.getElementById('div');
node.addEventListener('click', function() {
return text;
});
};
Remove event handlers when no longer needed
var object;
function createMemory() {
object = {
prop: new Array(10000000).join('x'),
run: function() {
var that = this;
setTimeout(function() {
that.run();
}, 10000);
}
};
object.run();
}
function freeMemory() {
object = null;
}
Take care of "that" in closure scope.
Fast object
var fastObject = {
prop1: 'string',
prop2: new Date()
};
// Fast operation
fastObject.prop1 = null;
Slow object
var slowObject = {
prop1: 'string',
prop2: new Date()
};
// Slow operation
delete slowObject.prop1;
// It creates 1000 objects and store them in a local cache
var pool = new SimpleMemoryPool({prop1: null, prop2: null}, 1000);
// Get an object from the pool, and assign the desired values
var myObject = pool.getInstance('an string', new Date());
// myObject = {prop1: 'an string', prop2: new Date()}
// When I don't need the object anymore, release it to the pool again
pool.release(myObject);
// prop1 and prop2 are nullified and object goes back to the pool
Use Chrome performance.memory API
Enable memory logging with --enable-precise-memory-info
Tip: Automatize your memory tests (Selenium...)