Plugging holes — javascript memory leak debugging
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

Plugging holes — javascript memory leak debugging

on

  • 385 views

 

Statistics

Views

Total Views
385
Views on SlideShare
378
Embed Views
7

Actions

Likes
0
Downloads
3
Comments
0

1 Embed 7

http://www.slideee.com 7

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Plugging holes — javascript memory leak debugging Presentation Transcript

  • 1. PLUGGING HOLES JAVASCRIPT MEMORY LEAK DEBUGGING Christian Speckner, Mayflower GmbH
  • 2. WHAT IS A MEMORY LEAK?
  • 3. WHAT DOES WIKIPEDIA SAY? “In computer science, a memory leak occurs when a computer program incorrectly manages memory allocations”
  • 4. MORE CONCISELY: Memory leaks occure if memory is repeatedly allocated without being freed again by the program.
  • 5. WHAT IS MEMORY ALLOCATION IN JAVASCRIPT? Memory allocation = Object creation: ... more ways to create objects varfoo=[1,2,3]; varfoo={bar:"baz"}; varfoo=newThing(); varfoo=function(){}; varfoo=document.createElement("div");
  • 6. HOW IS MEMORY FREED IN JAVASCRIPT? GARBAGE COLLECTION
  • 7. HOW DOES GARBAGE COLLECTION WORK? Objects are eligible for Garbage Collection once they cease to be "alive" Each object referenced by a "living" object is alive counted as alive The garbage collector roots are alive per definition → A living object is is part of a retaining tree which starts at a garbage collection root
  • 8. OBJECT REFERENCES Examples of Object References: Scope variables Object properties Function closures
  • 9. GARBAGE COLLECTOR ROOTS What are the garbage collector roots? Internals of the VM implementation For our purposes: windowcan be treated like a root ("dominator")
  • 10. MEMORY LEAKS IN JAVASCRIPT Memory leaks occur if objects are repeatedly created and cannot be reclaimed by the garbage collector due to remaining references.
  • 11. WHY SHOULD I CARE?
  • 12. THAT DEPENDS — HOW DOES YOUR APPLICATION LOOK LIKE?
  • 13. 1. LINKED WEBPAGES, A LITTLE JS ON EACH PAGE Navigating between pages resets the VM Script does not execute over extended periods of time Leaks have no time to cause trouble → NOTHING TO WORRY ABOUT
  • 14. 2. SINGLE PAGE JS APPLICATION All content generated by JS program Program runs as long as the page is open Small leaks accumulate Browser swallows 100s MB of RAM before crashing! → POTENTIAL SHOWSTOPPER!
  • 15. HOW TO AVOID LEAKS? Simple answer: Always be sure to delete all references to unused objects.
  • 16. REFERENCES VIA SCOPE VARIABLES functionfoo(){ vargarbage={dead:"meat"}; return"Helloworld!"; } foo(); 1. Object stays referenced through garbagewhile foo executes 2. garbagegoes out of scope once footerminates 3. No more references to object → can be garbage collected No big deal as long as we avoid global variables
  • 17. REFERENCES THROUGH OBJECT PROPERTIES varvault={}; functionfoo(){ vargarbage={dead:"meat"}; vault.entry=garbage; return"Helloworld!"; } foo(); Object remains referenced via vault.entryeven after footerminates Need either to reset vault.entryor remove all references to vault Take care that no other objects retain references to unused objects — maps / registries, I'm looking at you :)
  • 18. REFERENCES THROUGH FUNCTION CLOSURES functionfoo(){ vargarbage={dead:"meat"}; returnfunction(){ returngarbage.dead; }; } varbar=foo(); Object is referenced by the returned function closure even after footerminates! The object is only cleaned up after the function referenced by baris garbage collected With great power comes great responsibility: be careful when passing around closures!
  • 19. DANGEROUS SPECIAL CASE: EVENT HANDLERS functionCounter(elt){ varme=this; me.count=0; elt.addEventListener("mousedown",function(){ me.count++; } } varcounter=newCounter(document); The event handler keeps a reference to the counter instance We have no direct reference to the handler Garbage collection will never happen! Always leave a way to clean out event handlers (and do it!), don't throw away those references!
  • 20. DANGEROUS SPECIAL CASE: DETACHED DOM TREES vardiv=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 global registry... ... but remove it from the DOM when clearing its parent Node remains referenced → detached DOM tree! Take care to hold no references into DOM trees after they become detached!
  • 21. HOW TO FIND MEMORY LEAKS?
  • 22. GOOGLE CHROME TOOLS!
  • 23. MEMORY CONSUMPTION TIMELINE For a healthy application, memory consumption should Rise over time as objects are allocated Go down when garbage is collected → sawtooth pattern Baseline after garbage collection should not change Growing consumption after GC indicates a leak
  • 24. HEAP SNAPSHOTS Capture a snapshot of the heap contests Can be filtered by constructor Living objects can be inspected Allocated memory size Differences between snapshots: what leaks? Retaining trees: why does it leak? Retaining tree allows to track down the references that have to be cleaned out!
  • 25. SOME HINTS FOR READING THE RETAINING TREE The shortest path is usually most interesting Inspect what you can — know your enemy Follow the black objects; gray ones are internals 'context' indicates a function scope 'native' on a DOM node indicates an event handler Take your time!
  • 26. HEAP ALLOCATION TIMELINE Shows allocation events Allocations which have been freed turn light blue Heap can be inspected filtered based on the time of allocation Allows to correlate heap state with events → identify the code paths that leak
  • 27. SOME TRICKS
  • 28. TRACKING INDIVUAL OBJECTS functionMarker(id){ this.id=id; } vartrackedInstance=newThing(); trackedInstance.__marker=newMarker('someId'); Named constructors can be easily found in heap snapshots Marker is not collected as long as trackedInstance remains alive Allows to identify individual objects in heap More generally: naming function makes heap snapshots much easier to read
  • 29. DEALING WITH LEAKY LIBRARIES Common situation: varsnark=newlibrary.Snark(); snark.on('bark',someObject.handler,someObject); //... snark.destroy(); Assuming that the library is buggy, it could retain a reference to someObject, leading to a leak.
  • 30. Improvement: varsnark=newlibrary.Snark(), scope={instance:someObject}; snark.on('bark',function(){this.instance.handler()},scope); //... snark.destroy(); scope.instance=null; } Reference is now indirect via scope Resetting scope.instancebreaks reference The library can't prevent someObjectfrom being garbage collected Leak is reduced to scope object!
  • 31. GENERAL ADVICE Profile memory usage early and often Be extra careful with closures Unbind event listeners Be careful with registries Consider object pooling
  • 32. THAT'S ALL — THANK YOU FOR YOUR ATTENTION!