Det är värt att komma ihåg att även om det här verktyget kan vara mycket användbart för att hitta referenser till en misstänkt läcka, kan det vara ganska svårt och komplext att använda för att hitta okända läckor och kräver en hel del analys. Om du behärskar det är det dock ett bra verktyg att ha i bagaget!
LeakCanary
Ett annat sätt att hitta minnesläckor är genom att använda Leak Canary, ett bibliotek som skapats av Square och som hjälper dig att upptäcka minnesläckor. Det fungerar genom att använda en ObjectWatcher
klass som håller svaga referenser till förstörda objekt i heap. Om referensen inte rensas under de följande fem sekunderna och Garbage Collector har körts anses objektet behållas och loggar detta som en potentiell minnesläcka i logcat. Ganska smart, eller hur?
För att komma igång lägger du helt enkelt till biblioteket bland dina beroenden i din build.gradle-fil, synkroniserar och kompilerar.
Så snart du startar programmet kan du se LeakCanary-utgången i logcat när den bevakar instants och behåller objekt. För att utlösa en läcka lade jag till ett par nya uppgifter i min TaskManager-app. Inte långt därefter fick jag det här meddelandet från LeakCanary som indikerar en potentiell läcka!
Du klickar helt enkelt på meddelandet för att ”dumpa heap” för att undersöka den potentiella läckan och den här dialogrutan kommer att visas i appen.
Leak Canary analyserar sedan heap genom att lokalisera de behållna objekten och hittar läckagespåren, som är en sökväg med referenser till varje behållet objekt. När det är gjort visas ett meddelande som sammanfattar antalet kvarhållna objekt och läckor.
Det ser ut som om den hittade vår läcka! Om vi klickar på den ser vi hela läckagespåret:
Det säger oss att TaskActivity läcker och de understrukna referenserna visar spårningen, det läcker från mContext
i SingletonExample
– låter bekant! Nu vet vi hur man hittar en läcka med hjälp av LeakCanary. 😃
Undervika minnesläckor
Så nu när vi har hittat vår minnesläcka, hur kan vi undvika dem i framtiden? Nedan följer några av de vanligaste orsakerna och mönstren.
Sändningsmottagare
Sändningsmottagare kan användas för att lyssna på systemövergripande sändningshändelser eller intentioner som indikerar enhetsinformation, till exempel lågt batteri, datum och anslutningsändringar – till exempel att flygplansläget har stängts av. När vi använder dem måste vi komma ihåg att avregistrera sändningsmottagare, annars kommer vi oundvikligen att behålla en referens till aktiviteten.
Hur man undviker det:
Detta mönster finns också för asyncTask, TimerTask och trådar som måste avbrytas i onDestroy()
för att undvika läckage.
Kontext till Singleton-klass
Ibland behöver vi skicka in kontext från en aktivitet till en Singleton-klass. Ett exempel på detta är en utils-klass där vi behöver få tillgång till resurser, tjänster eller interna filer. Att överföra kontext innebär dock att vi oundvikligen håller fast vid en referens till aktiviteten.
Hur man undviker det: Istället för att skicka in this
från en aktivitet kan vi skicka in applikationskontext om den är tillgänglig (om du vill lära dig mer om när du ska använda vilken kontext, fann jag den här artikeln väldigt hjälpsam!). En alternativ lösning är att se till att vi sätter Singleton-kontexten till null inuti aktiviteten onDestroy()
-metoden.
Statiska referenser
Att referera en vy eller en aktivitet som statisk innebär att referensen till aktiviteten inte kommer att sopas upp. Detta bör helt enkelt undvikas hela tiden.
Hur man undviker det: Om du av någon anledning måste göra det kan du se till att den förstörs genom att sätta den till null i onDestroy().
Inre klassreferenser
Inre klasser orsakar ofta läckor genom att hålla en implicit referens till den yttre klassen. Detta händer om klassvariabeln deklareras som statisk eller om själva klassen inte deklareras som statisk. Förvirrande? Ja, men det är lätt att undvika om vi följer den enkla regeln nedan.
Hur man undviker det: Gör den inre klassen statisk för att undvika att hålla en referens till den yttre klassen och skapa aldrig en statisk variabel för en inre klass. Samma sak gäller för anonyma klasser.
Det var allt! Hoppas du har lärt dig några saker om minnesläckor och hur man undviker dem. Lycka till med kodningen! 😄
Här är ett antal artiklar och dokumentation som jag fann särskilt användbara när jag lärde mig om minnesläckor: