Upload
gonzalo-ruiz-de-villa
View
9.778
Download
1
Tags:
Embed Size (px)
DESCRIPTION
JavaScript applications are increasingly larger and they also tend to execute during longer periods. In these applications, the memory is a scarce and valuable resource that must be taken care of. With the support of the Chrome Dev Tools, we will learn how to analyze memory consumption and how to find and fix memory leaks, making our applications more stable and robust in the way.
Citation preview
Findinganddebugging
memoryleaksinJavaScriptwith
ChromeDevTools
GonzaloRuizdeVilla@gruizdevilla
Ico-founded&work@adesis
Thispresentationwasmakeformyworkshopat#spainjs2013
$whoami
$aboutthis
Whatisamemoryleak?
Graduallossofavailablecomputermemory
whenaprogramrepeatedly
failstoreturnmemorythatithasobtainedfortemporaryuse.
Myusershavelaptopswith16GBofRAM.
So,whyshouldIcare?
Commonbelief
Morememory===Betterperformance
Reality
Memoryfootprint
isstronglycorrelatedwith
increasedlatenciesandvariance
Nothingisfree:
(cheaporexpensive)
youwillalwayspayapricefortheresourcesyouuse
So,let'stalkaboutmemory
Thinkofmemoryasagraph
Threeprimitivetypes:
Numbers(e.g,3.14159...)Booleans(trueorfalse)
Strings(e.g,"WernerHeisenberg")
Theycannotreferenceothervalues.Theyarealwaysleafsorterminatingnodes.
Everythingelseisan"Object"
Objectsareassociativearrays(mapsordictionaries)
So,theobjectiscomposedofacollectionof(key,value)pairs
AndwhataboutArrays?
AnArrayisanObjectwithnumerickeys.
Thememorygraphstartswitharoot
Itmaybethe objectofthebrowser,orthe objectofaNode.jsmodule.
windowGlobalYoudon'tcontrolhowthisrootobjectisGC
WhatdoesgetGC?Whateverisnotreachablefromtheroot.
RetainingpathWecallaretainingpathanypathfromGCrootstoaparticularobject
Dominators
Node1dominatesnode2Node2dominatesnodes3,4and6Node3dominatesnode5Node5dominatesnode8Node6dominatesnode7
SomefactsabouttheV8GarbageCollector
GenerationalCollector
Ageofavalue
YoungGeneration
OldGeneration
Theageofavalue:numberofbytesallocatedsinceitwasallocated.
Splitedintwospaces:named"to"and"from""tospace":veryfastallocationfillingthe"tospace"triggersacollection:
"to"and"from"swapmaybepromotiontooldgeneration~10ms(remember60fps->~16ms)
Oldgenerationcollectionisslow.
"To"and"From"spaces
Remember:triggeringa
collectionpausesyourapplication.
Somede-referencecommonerrors
Becarefulwitthedeletekeyword.
"o"becomesanSLOWobject.
varo={x:"y"};deleteo.x;o.x;//undefined
varo={x:"y"};o=null;o.x;//TypeError
Itisbettertoset"null".
Onlywhenthelastreferencetoanobjectisremoved,isthatobjecteligibleforcollection.
Awordon"slow"objectsV8optimizingcompilermakesassumptionsonyourcodetomakeoptimizations.
Ittransparentlycreateshiddenclassesthatrepresentyourobjects.
Usingthishiddenclasses,V8worksmuchfaster.Ifyou"delete"properties,theseassumptionsarenolongervalid,andthecodeisde-optimized,slowingyourcode.
FastObject SlowObject
"slow"shouldbeusingasmallermemoryfootprintthan"fast"(1lessproperty),shouldn'tit?
functionSlowPurchase(units,price){this.units=units;this.price=price;this.total=0;this.x=1;}varslow=newSlowPurchase(3,25);//xpropertyisuseless//soIdeleteitdeleteslow.x;
"fast"objectsarefaster
functionFastPurchase(units,price){this.units=units;this.price=price;this.total=0;this.x=1;}varfast=newFastPurchase(3,25);
REALITY:"SLOW"isusing15timesmorememory
TimersTimersareaverycommonsource
ofmemoryleaks.Lookatthefollowingcode:
Ifwerun:Withthiswehaveamemoryleak:
varbuggyObject={callAgain:function(){varref=this;varval=setTimeout(function(){console.log('Calledagain:'+newDate().toTimeString());ref.callAgain();},1000);}};
buggyObject.callAgain();buggyObject=null;
ClosuresClosurescanbeanothersourceofmemoryleaks.Understandwhat
referencesareretainedintheclosure.
Andremember:evalisevil
vara=function(){varlargeStr=newArray(1000000).join('x');returnfunction(){returnlargeStr;};}();
vara=function(){varsmallStr='x',largeStr=newArray(1000000).join('x');returnfunction(n){returnsmallStr;};}();
vara=function(){varsmallStr='x',largeStr=newArray(1000000).join('x');returnfunction(n){eval('');//maintainsreferencetolargeStrreturnsmallStr;};}();
DOMleaksarebiggerthanyouthinkWhenisthe#treeGC?
#leafmaintainsareferencetoit'sparent(parentNode),andrecursivelyupto#tree,soonlywhenleafRefisnullifiedistheWHOLEtreeunder#treecandidatetobeGC
varselect=document.querySelector;vartreeRef=select("#tree");varleafRef=select("#leaf");varbody=select("body");body.removeChild(treeRef);//#treecan'tbeGCyetduetotreeReftreeRef=null;//#treecan'tbeGCyet,dueto//indirectreferencefromleafRefleafRef=null;//NOWcanbe#treeGC
E
Rulesofthumb
Useappropiatescope
Unbindeventlisteners
Managelocalcache
Betterthande-referencing,uselocalscopes.
Unbindeventsthatarenolongerneeded,speciallyiftherelatedDOMobjectsaregoingtoberemoved.
Becarefulwithstoringlargechunksofdatathatyouarenotgoingtouse.
ObjectPoolsYounggenerationGCtakesabout10ms.
Maybeitistoomuchtimeforyou:
Insteadofallocatinganddeallocatingobjects,reusethemwithobjectpools.
Note:objectpoolshavetheirowndrawbacks(forexample,cleaningusedobjects)
Threekeyquestions1. Areyouusingtoomuchmemory?
2. Doyouhavememoryleaks?
3. IsyourappGCingtoooften?
Knowingyourarsenal
BrowserInfoYoucanmeasurehowyourusersareusing
memory.
Youcanmonitortheiractivitytodetect
unexpecteduseofmemory
(onlyinChrome)
>performance.memoryMemoryInfo{jsHeapSizeLimit:793000000,usedJSHeapSize:27600000,totalJSHeapSize:42100000}
jsHeapSizeLimit
usedJSHeapSize
totalJSHeapSize
theamountofmemorythatJavaScriptheapislimitedto
theamountofmemorythatJavaScripthasallocated(includingfreespace)
theamountofmemorycurrentlybeingused
IfusedJSHeapSizegrowsclosetojsHeapSizeLimitthereisariskof:
Imean...
ChromeDevToolsCtrl+Shift+I
⌥⌘Ihttps://developers.google.com/chrome-developer-tools/
Memorytimeline
MemoryProfilingTakingsnapshots
ReadingyourresultsSummary
EYE-CATCHINGTHINGSINTHESUMMARY
Distance:distancefromtheGCroot.
Ifalmostalltheobjectsofthesametypeareatthesamedistance,
andafewareatabiggerdistance,that'ssomethingworthinvestigating.
Areyouleakingthelatterones?
MOREEYE-CATCHINGTHINGSINTHESUMMARY
Retainingmemory:thememoryusedbytheobjects
ANDtheobjectstheyarereferencing.Useittoknowwhereareyouusingmostofthememory.
ATIPABOUTCLOSURESIthelpsalottonamethefunctions,soyoueasilydistinguishbetween
closuresinthesnapshot.functioncreateLargeClosure(){varlargeStr=newArray(1000000).join('x');varlC=function(){//thisISNOTanamedfunctionreturnlargeStr;};returnlC;}
functioncreateLargeClosure(){varlargeStr=newArray(1000000).join('x');varlC=functionlC(){//thisISanamedfunctionreturnlargeStr;};returnlC;}
Switchingbetweensnapshotsviews
Summary:groupsbyconstructornameComparison:comparestwosnapshots
Containment:bird'seyeviewoftheobjectstructureDominators:usefultofindaccumulationpoints
Understandingnodecolors
Yellow:objecthasaJavaScriptreferenceonit
Red:detachednode.Referencedfromonewithyellowbackground
YoucanforceGCfromChromeDevTools
WhentakingaHeapSnapshot,itisautomaticallyforced.InTimeline,itcanbeveryconvenienttoforceaGC.
MemoryleakpatternSomenodesarenotbeingcollected:
The3snapshottechnique
Rationale
Yourlongrunningapplicationisinanstationarystate.
Memoryoscillatesaroundaconstantvalue.
(orhasaconstant,controlled,expectedandjustifiedgrowth).
Whatdoweexpect?
Newobjectstobeconstantlyandconsistentlycollected.
Let'ssaywestartfromasteadystate:
Checkpoint#1
WedosomestuffCheckpoint#2
WerepeatthesamestuffCheckpoint#3
Again,whatshouldweexpect?
AllnewmemoryusedbetweenCheckpoint#1andCheckpoint#2
hasbeencollected.
NewmemoryusedbetweenCheckpoint#2andCheckpoint#3maystillbeinuseinCheckpoint
#3
ThestepsOpenDevToolsTakeaheapsnapshot#1PerformsuspiciousactionsTakeaheapsnapshot#2PerformsameactionsagainTakeathirdheapsnapshot#3Selectthissnapshot,andselect"ObjectsallocatedbetweenSnapshots1and2"
The3snapshottechniqueevolved
Simpler&morepowerfulbut...Doyou
haveChromeCanaryinstalled?
Brandnewfeature:
RecordHeapAllocations
Bluebars:memoryallocations.Tallerequalsmorememory.Greybars:deallocated
Let'splay!Youcangetthecodefrom:
https://github.com/gonzaloruizdevilla/debuggingmemory.git
Oryoucanuse:
http://goo.gl/4SK53