plugging holes — javascript memory leak debugging

32
PLUGGING HOLES JAVASCRIPT MEMORY LEAK DEBUGGING Christian Speckner, Mayflower GmbH

Upload: mayflower-gmbh

Post on 29-Nov-2014

532 views

Category:

Software


2 download

DESCRIPTION

 

TRANSCRIPT

Page 1: Plugging holes — javascript memory leak debugging

PLUGGINGHOLES

JAVASCRIPT MEMORY LEAKDEBUGGING

Christian Speckner, Mayflower GmbH

Page 2: Plugging holes — javascript memory leak debugging

WHAT IS A MEMORYLEAK?

Page 3: Plugging holes — javascript memory leak debugging

WHAT DOES WIKIPEDIA SAY?“In computer science, a memory leak

occurs when a computer programincorrectly manages memory

allocations”

Page 4: Plugging holes — javascript memory leak debugging

MORE CONCISELY:Memory leaks occure if memory is repeatedly allocated

without being freed again by the program.

Page 5: Plugging holes — javascript memory leak debugging

WHAT IS MEMORY ALLOCATIONIN JAVASCRIPT?

Memory allocation = Object creation:

... more ways to create objects

var foo = [1, 2, 3];

var foo = {bar: "baz"};

var foo = new Thing();

var foo = function() {};

var foo = document.createElement("div");

Page 6: Plugging holes — javascript memory leak debugging

HOW IS MEMORY FREED INJAVASCRIPT?

GARBAGE COLLECTION

Page 7: Plugging holes — javascript memory leak debugging

HOW DOES GARBAGECOLLECTION WORK?

Objects are eligible for Garbage Collection once theycease to be "alive"

Each object referenced by a "living" object is alivecounted as aliveThe garbage collector roots are alive per definition

→ A living object is is part of a retaining tree which startsat a garbage collection root

Page 8: Plugging holes — javascript memory leak debugging

OBJECT REFERENCESExamples of Object References:

Scope variablesObject propertiesFunction closures

Page 9: Plugging holes — javascript memory leak debugging

GARBAGE COLLECTOR ROOTSWhat are the garbage collector roots?

Internals of the VM implementationFor our purposes: window can be treated like a root("dominator")

Page 10: Plugging holes — javascript memory leak debugging

MEMORY LEAKS INJAVASCRIPT

Memory leaks occur if objects are repeatedly created andcannot be reclaimed by the garbage collector due to

remaining references.

Page 11: Plugging holes — javascript memory leak debugging

WHY SHOULD I CARE?

Page 12: Plugging holes — javascript memory leak debugging

THAT DEPENDS—

HOW DOES YOURAPPLICATION LOOK

LIKE?

Page 13: Plugging holes — javascript memory leak debugging

1. LINKED WEBPAGES, A LITTLE JSON EACH PAGE

Navigating between pages resets the VMScript does not execute over extended periods of timeLeaks have no time to cause trouble

→ NOTHING TO WORRY ABOUT

Page 14: Plugging holes — javascript memory leak debugging

2. SINGLE PAGE JS APPLICATION

All content generated by JS programProgram runs as long as the page is openSmall leaks accumulateBrowser swallows 100s MB of RAM before crashing!

→ POTENTIAL SHOWSTOPPER!

Page 15: Plugging holes — javascript memory leak debugging

HOW TO AVOID LEAKS?Simple answer: Always be sure to delete all references to

unused objects.

Page 16: Plugging holes — javascript memory leak debugging

REFERENCES VIA SCOPEVARIABLES

function foo() { var garbage = {dead: "meat"}; return "Hello world!";}

foo();

1. Object stays referenced through garbage while fooexecutes

2. garbage goes out of scope once foo terminates3. No more references to object → can be garbage

collected

No big deal as long as we avoid global variables

Page 17: Plugging holes — javascript memory leak debugging

REFERENCES THROUGH OBJECTPROPERTIES

var vault = {};function foo() { var garbage = {dead: "meat"}; vault.entry = garbage; return "Hello world!";}

foo();

Object remains referenced via vault.entry even afterfoo terminatesNeed either to reset vault.entry or remove allreferences to vault

Take care that no other objects retain references tounused objects — maps / registries, I'm looking at you :)

Page 18: Plugging holes — javascript memory leak debugging

REFERENCES THROUGHFUNCTION CLOSURES

function foo() { var garbage = {dead: "meat"}; return function() { return garbage.dead; };}

var bar = foo();

Object is referenced by the returned function closureeven after foo terminates!The object is only cleaned up after the functionreferenced by bar is garbage collected

With great power comes great responsibility: be carefulwhen passing around closures!

Page 19: Plugging holes — javascript memory leak debugging

DANGEROUS SPECIAL CASE:EVENT HANDLERS

function Counter(elt) { var me = this; me.count = 0;

elt.addEventListener("mousedown", function() { me.count++; }}var counter = new Counter(document);

The event handler keeps a reference to the counterinstanceWe have no direct reference to the handlerGarbage collection will never happen!

Always leave a way to clean out event handlers (and doit!), don't throw away those references!

Page 20: Plugging holes — javascript memory leak debugging

DANGEROUS SPECIAL CASE:DETACHED DOM TREES

var div = document.createElement('div');myFancyElementRegistry.register(div, 'somediv');document.querySelector('.container').appendChild(div);

// ...

document.querySelector('.container').innerHTML = '';

We keep a reference to the newly created div in a globalregistry...... but remove it from the DOM when clearing its parentNode remains referenced → detached DOM tree!

Take care to hold no references into DOM trees after theybecome detached!

Page 21: Plugging holes — javascript memory leak debugging

HOW TO FIND MEMORYLEAKS?

Page 22: Plugging holes — javascript memory leak debugging

GOOGLE CHROMETOOLS!

Page 23: Plugging holes — javascript memory leak debugging

MEMORY CONSUMPTIONTIMELINE

For a healthy application, memory consumption shouldRise over time as objects are allocatedGo down when garbage is collected → sawtooth patternBaseline after garbage collection should not change

Growing consumption after GC indicates a leak

Page 24: Plugging holes — javascript memory leak debugging

HEAP SNAPSHOTSCapture a snapshot of the heap contestsCan be filtered by constructorLiving objects can be inspectedAllocated memory sizeDifferences between snapshots: what leaks?Retaining trees: why does it leak?

Retaining tree allows to track down the references thathave to be cleaned out!

Page 25: Plugging holes — javascript memory leak debugging

SOME HINTS FOR READING THERETAINING TREE

The shortest path is usually most interestingInspect what you can — know your enemyFollow the black objects; gray ones are internals'context' indicates a function scope'native' on a DOM node indicates an event handlerTake your time!

Page 26: Plugging holes — javascript memory leak debugging

HEAP ALLOCATION TIMELINEShows allocation eventsAllocations which have been freed turn light blueHeap can be inspected filtered based on the time ofallocation

Allows to correlate heap state with events → identify thecode paths that leak

Page 27: Plugging holes — javascript memory leak debugging

SOME TRICKS

Page 28: Plugging holes — javascript memory leak debugging

TRACKING INDIVUAL OBJECTSfunction Marker(id) { this.id = id;}

var trackedInstance = new Thing();trackedInstance.__marker = new Marker('someId');

Named constructors can be easily found in heapsnapshotsMarker is not collected as long as trackedInstanceremains aliveAllows to identify individual objects in heap

More generally: naming function makes heap snapshotsmuch easier to read

Page 29: Plugging holes — javascript memory leak debugging

DEALING WITH LEAKY LIBRARIESCommon situation:

var snark = new library.Snark();snark.on('bark', someObject.handler, someObject);

// ...

snark.destroy();

Assuming that the library is buggy, it could retain areference to someObject, leading to a leak.

Page 30: Plugging holes — javascript memory leak debugging

Improvement:var snark = new library.Snark(), scope = {instance: someObject};

snark.on('bark', function() {this.instance.handler()}, scope);

// ...

snark.destroy();scope.instance = null;}

Reference is now indirect via scopeResetting scope.instance breaks referenceThe library can't prevent someObject from beinggarbage collectedLeak is reduced to scope object!

Page 31: Plugging holes — javascript memory leak debugging

GENERAL ADVICEProfile memory usage early and oftenBe extra careful with closuresUnbind event listenersBe careful with registriesConsider object pooling

Page 32: Plugging holes — javascript memory leak debugging

THAT'S ALL —

THANK YOU FOR YOURATTENTION!