Inspektera din apps minnesanvändning med Memory Profiler

Memory Profiler är en komponent i Android Profiler som hjälper dig att identifiera minnesläckor och minnesförluster som kan leda till att appen stannar, fryser och till och med kraschar. Den visar en realtidsgraf över appens minnesanvändning och låter dig fånga en heapdump, tvinga fram skräpplockning och spåra minnesallokeringar.

Följ dessa steg för att öppna Memory Profiler:

  1. Klicka på Visa > Verktygsfönster > Profiler (du kan också klicka på Profile i verktygsfältet).
  2. Välj den enhet och den app-process du vill profilera i verktygsfältet för AndroidProfiler. Om du har anslutit en enhet via USB men inte ser den i listan ska du se till att du har aktiverat USB-debuggning.
  3. Klicka var som helst på tidslinjen MEMORY för att öppna Memory Profiler.

Alternativt kan du inspektera appens minne från kommandoraden meddumpsys, och även se GC-händelser i logcat.

Varför du bör profilera minnet i din app

Android tillhandahåller en administrerad minnesmiljö – när den fastställer att din app inte längre använder vissa objekt släpper garbage collector det oanvända minnet tillbaka till heap. Hur Android går tillväga för att hitta oanvänt minne förbättras ständigt, men vid någon tidpunkt i alla Androidversioner måste systemet göra en kort paus i din kod. För det mesta är pauserna omöjliga att uppfatta. Men om din app allokerar minne snabbare än vad systemet kan samla in det, kan det hända att din app fördröjs medan samlaren frigör tillräckligt med minne för att tillgodose dina allokeringar. Fördröjningen kan leda till att din app hoppar över ramar och orsakar synlig långsamhet.

Även om din app inte uppvisar långsamhet kan den, om den läcker minne, behålla det minnet även när den är i bakgrunden. Detta beteende kan göra resten av systemets minnesprestanda långsammare genom att tvinga fram onödiga skräpinsamlingshändelser. Till slut tvingas systemet att döda app-processen för att återta minnet. När användaren sedan återvänder till appen måste den starta om helt och hållet.

För att förebygga dessa problem bör du använda Memory Profiler för att göra följande:

  • Leta efter oönskade minnesallokeringsmönster i tidslinjen som kan orsaka prestandaproblem.
  • Dump Java-heap för att se vilka objekt som förbrukar minnet vid varje given tidpunkt. Flera heapdumpar under en längre tidsperiod kan hjälpa till att identifiera minnesläckor.
  • Registrera minnesallokeringar under normal och extrem användarinteraktion för att identifiera exakt var din kod antingen allokerar för många objekt på kort tid eller allokerar objekt som blir läckta.

För information om programmeringsmetoder som kan minska appens minnesanvändning, läs Hantera appens minne.

Översikt över Memory Profiler

När du öppnar Memory Profiler för första gången ser du en detaljerad tidslinje över appens minnesanvändning och får tillgång till verktyg för att tvinga fram skräpplockning, fånga en heapdump och registrera minnesallokeringar.

Figur 1. Memory Profiler

Som framgår av figur 1 innehåller standardvyn för Memory Profiler följande:

  1. En knapp för att tvinga fram en skräpplockningshändelse.
  2. En knapp för att fånga en heapdump.

    Notera: En knapp för att registrera minnesallokeringar visas till höger om knappen för heapdump endast när den är ansluten till en enhet som kör Android 7.1 (API-nivå 25) eller lägre.

  3. En rullgardinsmeny för att ange hur ofta profileraren ska registrera minnesallokeringar. Att välja lämpligt alternativ kan hjälpa dig att förbättra appens prestanda under profileringen.
  4. Knappar för att zooma in/ut på tidslinjen.
  5. En knapp för att hoppa framåt till de levande minnesdata.
  6. Händelsetidslinjen, som visar aktivitetstillstånd, händelser för användarinmatning och händelser för skärmens rotation.
  7. Tidslinjen för minnesanvändning, som innehåller följande:
    • En staplad graf som visar hur mycket minne som används av varje minneskategori, vilket indikeras av y-axeln till vänster och färgnyckeln överst.
    • En streckad linje visar antalet allokerade objekt, vilket indikeras av y-axeln till höger.
    • En ikon för varje skräpinsamlingshändelse.

Om du använder en enhet som kör Android 7.1 eller lägre är dock inte alla profileringsdata synliga som standard. Om du ser meddelandet ”Avancerad profilering är inte tillgänglig för den valda processen” måste du aktivera avancerad profilering för att se följande:

  • Händelsetidlinje
  • Antal allokerade objekt
  • Händelser för skräpplockning

På Android 8.0 och högre är avancerad profilering alltid aktiverad för debuggableappar.

Hur minnet räknas

Siffrorna du ser högst upp i Memory Profiler (figur 2) är baserade på alla privata minnessidor som din app har engagerat, enligtAndroid-systemet. Denna räkning omfattar inte sidor som delas med systemet eller andra appar.

Figur 2. Legenden om minnesräkning högst upp i Memory Profiler

Kategorierna i minnesräkningen är följande:

  • Java: Minne från objekt som allokerats från Java- eller Kotlin-kod.
  • Native: Minne från objekt som allokerats från Java- eller Kotlin-kod: Även om du inte använder C++ i din app kan du se att det används en del inhemskt minne här eftersom Android-ramverket använder inhemskt minne för att hantera olika uppgifter för din räkning, t.ex. vid hantering av bildtillgångar och annan grafik – även om koden du har skrivit i Java eller Kotlin.

  • Grafik: Minne från objekt som allokerats från C- eller C++-kod: Minne som används för grafiska buffertköer för att visa pixlar på skärmen, inklusive GL-ytor, GL-texturer och så vidare. (Observera att detta är minne som delas med CPU, inte dedikerat GPU-minne.)

  • Stack: Minne som används av både infödda och Java-stackar i din app. Detta har vanligtvis att göra med hur många trådar din app körs.

  • Kod: Minne som din app använder för kod och resurser, t.ex. dex-bytecode, optimerad eller kompilerad dex-kod, .so-bibliotek och teckensnitt.

  • Övrigt: Minne som din app använder för kod och resurser, t.ex. dex-bytecode, optimerad eller kompilerad dex-kod, .so-bibliotek och teckensnitt: Minne som används av programmet och som systemet inte vet hur det ska kategoriseras.

  • Allokerat: Antalet Java/Kotlin-objekt som allokerats av din app. Här räknas inte objekt som allokerats i C eller C++.

    När du är ansluten till en enhet som kör Android 7.1 och lägre börjar den här allokeringsräkningen endast vid den tidpunkt då Memory Profiler anslöt till din körda app. Så alla objekt som allokeras innan du börjar profilera räknas inte med. Android 8.0 och högre innehåller dock ett profileringsverktyg på enheten som håller reda på alla allokeringar, så det här antalet representerar alltid det totala antalet utestående Java-objekt i din app på Android 8.0 och högre.

När man jämför med minnesräkningar från det tidigare verktyget Android Monitor registrerar den nyaMemory Profiler ditt minne på ett annat sätt, så det kan tyckas som om dinmemory-användning nu är högre. Memory Profiler övervakar några extra kategorier som ökar totalsumman, men om du bara bryr dig om minnet i Java-heap bör ”Java”-siffran vara likadan som värdet från det tidigare verktyget.Även om Java-siffran förmodligen inte exakt stämmer överens med vad du såg i AndroidMonitor, så tar den nya siffran hänsyn till alla fysiska minnessidor som har allokerats till appens Java-heap sedan den forkades från Zygote. Detta ger alltså en korrekt bild av hur mycket fysiskt minne din app faktiskt använder.

Se minnesallokeringar

Minnesallokeringar visar hur varje Java-objekt och JNI-referens i ditt minne allokerades. Memory Profiler kan visa följande om objektallokeringar:

  • Vilka typer av objekt som allokerades och hur mycket utrymme de använder.
  • Stacktrace för varje allokering, inklusive i vilken tråd.
  • När objekten avallokerades (endast om du använder en enhet med Android 8.0 eller högre).

Om din enhet kör Android 8.0 eller högre kan du visa dina objektallokeringar när som helst enligt följande: Dra i tidslinjen för att välja den region som du vill visa tilldelningarna för (som visas i video 1). Du behöver inte påbörja en inspelningssession eftersom Android 8.0 och senare innehåller ett verktyg för profilering som inte finns på enheten och som ständigt spårar appens allokeringar.

Video 1. Med Android 8.0 och högre väljer du ett befintligt tidslinjeområde för att visa objektallokeringar

Om enheten kör Android 7.1 eller lägre klickar du på Spela in minnesallokeringar i verktygsfältet Memory Profiler. Under inspelningen spårar Memory Profiler alla allokeringar som sker i din app. När du är klar klickar du på Stoppa inspelning (samma knapp; se video 2) för att visa allokeringarna.

Video 2. Med Android 7.1 och lägre måste du explicit registrera minnesallokeringar

När du väljer ett område på tidslinjen (eller när du avslutar en inspelningssession med en enhet som kör Android 7.1 eller lägre) visas listan över allokerade objekt under tidslinjen, grupperade efter klassnamn och sorterade efter derasheap count.

Följ dessa steg för att inspektera allokeringsregistreringen:

  1. Bläddra i listan för att hitta objekt som har ovanligt stora heap counts och som kan ha läckt ut. Om du vill hitta kända klasser kan du klicka på kolumnrubriken Klassnamn för att sortera i alfabetisk ordning. Klicka sedan på ett klassnamn. FönstretInstance View visas till höger och visar varje instans av den klassen, enligt figur 3.
    • Alternativt kan du hitta objekt snabbt genom att klicka på Filter, eller genom att trycka på Control+F (Command+F på Mac) och skriva in ett klass- eller paketnamn i sökfältet. Du kan också söka efter metodnamn om du väljerArrange by callstack från rullgardinsmenyn. Om du vill använda reguljära uttryck kryssar du i rutan bredvid Regex. Markera rutan bredvidMatch case om din sökfråga är skiftlägeskänslig.
  2. I fönstret Instance View klickar du på en instans. Fliken Call Stack visas nedan och visar var instansen allokerades och i vilken tråd.
  3. I fliken Call Stack högerklickar du på en rad och väljerJump to Source (Hoppa till källan) för att öppna koden i redigeraren.

Figur 3. Detaljer om varje allokerat objekt visas i instansvyn till höger

Du kan använda de två menyerna ovanför listan över allokerade objekt för att välja vilken heap som ska inspekteras och hur data ska organiseras.

I menyn till vänster väljer du vilken heap som ska inspekteras:

  • standardheap: När ingen heap anges av systemet.
  • image heap: Systemets uppstartsavbild, som innehåller klasser som laddas in under uppstarten. Allokeringar här är garanterade att aldrig flyttas eller försvinna.
  • zygote heap: Zygote: Den heap där en app-process förgrenas från i Android-systemet.
  • app heap: Den heap där en app-process förgrenas från i Android-systemet:
  • JNI-heap: Den primära heap på vilken din app allokerar minne: JNI: Heap som visar var JNI-referenser (Java Native Interface) allokeras och släpps.

I menyn till höger väljer du hur du vill ordna allokeringarna:

  • Ordna efter klass: Grupperar alla tilldelningar baserat på klassnamn. Detta är standard.
  • Ordna efter paket:
  • Ordna efter callstack:

Förbättra appens prestanda vid profilering

För att förbättra appens prestanda vid profilering, samplar minnesprofileraren minnesallokeringar med jämna mellanrum som standard. När du testar på enheter som kör APIlevel 26 eller högre kan du ändra det här beteendet genom att använda rullgardinsmenyn Allocation Tracking. Följande alternativ är tillgängliga:

  • Fullständigt: Fångar alla objektallokeringar i minnet. Detta är standardbeteendet i Android Studio 3.2 och tidigare. Om du har en app som allokerar många objekt kan du observera synliga fördröjningar i din app vid profilering.
  • Samplad: Provar objektallokeringar i minnet med jämna mellanrum. Detta är standardalternativet och har mindre inverkan på appens prestanda vid profilering. Appar som allokerar många objekt på kort tid kan ändå uppvisa synliga fördröjningar.
  • Av: Slutar spåra appens minnesallokering.

Se globala JNI-referenser

Java Native Interface (JNI) är ett ramverk som gör det möjligt för Javakod och inhemsk kod att anropa varandra.

JNI-referenser hanteras manuellt av den inhemska koden, så det är möjligt attJava-objekt som används av inhemsk kod hålls vid liv för länge. Vissa objekt på Java-heap kan bli oåtkomliga om en JNI-referens slängs utan att först uttryckligen raderas. Det är också möjligt att den globala gränsen för JNI-referenser är uttömd.

För att felsöka sådana problem kan du använda vyn JNI heap i Memory Profiler för att söka igenom alla globala JNI-referenser och filtrera dem efter Java-typer och inhemsk anropstack. Med den här informationen kan du ta reda på när och var globala JNI-referenser skapas och tas bort.

Medans appen körs väljer du en del av tidslinjen som du vill inspektera och väljer JNI heap från rullgardinsmenyn ovanför klasslistan.Du kan sedan inspektera objekten i heap som vanligt och dubbelklicka på objekt i fliken Allocation Call Stack för att se var JNI-referenserna allokeras och släpps i din kod, vilket visas i figur 4.

Figur 4. Visa globala JNI-referenser

För att inspektera minnesallokeringar för appens JNI-kod måste du distribuera appen till en enhet som kör Android 8.0 eller högre.

För mer information om JNI, se JNI-tips.

Native Memory Profiler

Android Studio Memory Profiler innehåller en Native Memory Profiler för appar som distribueras till fysiska enheter som kör Android 10. Stöd för Android 11-enheter finns för närvarande tillgängligt i förhandsversionen av Android Studio 4.2.

Native Memory Profiler spårar allokeringar/deallokeringar av objekt i nativekod under en viss tidsperiod och ger följande information:

  • Allokeringar: En räkning av objekt som allokerats via malloc() eller newoperatorn under den valda tidsperioden.
  • Deallokeringar: En räkning av objekt som allokerats via malloc() eller newoperatorn under den valda tidsperioden:
  • Tilldelningar: Antal objekt som avallokerats via free() eller operatorndelete under den valda tidsperioden.
  • Tilldelningar: Storlek:
  • Storlek på tilldelningar: Den sammanlagda storleken i byte för alla tilldelningar under den valda tidsperioden.
  • Storlek på deallokeringar: Den sammanlagda storleken i byte för alla tilldelningar under den valda tidsperioden: Den sammanlagda storleken i byte för allt frigjort minne under den valda tidsperioden.
  • Totalt antal: Värdet i kolumnen Tilldelningar minus värdet i kolumnen Avdelningar.
  • Återstående storlek: Om du vill påbörja en inspelning klickar du på Record native allocations högst upp i fönstretMemory Profiler:

    När du vill avsluta inspelningen klickar du på Stop recording.

    När du vill avsluta inspelningen klickar du på Stop recording.

    Som standard använder Native Memory Profiler en samplingstorlek på 32 bytes: Varje gång 32 byte minne allokeras tas en ögonblicksbild av minnet. En mindre provstorlek resulterar i mer frekventa ögonblicksbilder, vilket ger mer exakta data om minnesanvändning. En större provstorlek ger mindre exakta data, men förbrukar mindre resurser i systemet och förbättrar prestandan vid inspelning.

    För att ändra provstorleken för Native Memory Profiler:

    1. Välj Kör > Redigera konfigurationer.
    2. Välj din appmodul i den vänstra rutan.
    3. Klicka på fliken Profilering och ange provstorleken i fältet som heterNative memory sampling interval (bytes).
    4. Bygg och kör appen igen.

    Fånga en heapdump

    En heapdump visar vilka objekt i appen som använder minne vid den tidpunkt då du fångar heapdumpningen. Särskilt efter en längre användarsession kan en heapdump hjälpa till att identifiera minnesläckor genom att visa objekt som fortfarande finns i minnet och som du tror inte längre borde finnas där.

    När du har fångat en heapdump kan du se följande:

    • Vilka typer av objekt som appen har allokerat, och hur många av varje.
    • Hur mycket minne varje objekt använder.
    • Hur referenser till varje objekt hålls i din kod.
    • Kallningsstapeln för var ett objekt allokerades. (Anropsstackar är för närvarande endast tillgängliga med en heap-dump med Android 7.1 och lägre när du fångar heap-dumpen samtidigt som du registrerar allokeringar.)

    För att fånga en heap-dump klickar du på Dump Java heap i verktygsfältet Memory Profiler.Under dumpningen av heap kan mängden Javaminne öka tillfälligt.Detta är normalt eftersom heapdumpningen sker i samma process som din app och kräver en del minne för att samla in data.

    Heapdumpningen visas under minnestidslinjen och visar alla klasstyper iheap, vilket visas i figur 5.

    Figur 5. Visning av heapdump

    Om du behöver vara mer exakt när dumpningen skapas kan du skapa en heapdump vid den kritiska punkten i din applikationskod genom att anropadumpHprofData().

    I listan över klasser kan du se följande information:

    • Allokeringar: Antal allokeringar i heap.
    • Nativ storlek: Totalt antal inhemskt minne som används av den här objekttypen (i byte). Den här kolumnen är endast synlig för Android 7.0 och högre.

      Du kommer att se minne här för vissa objekt som allokerats i Java eftersom Androidanvänder inhemskt minne för vissa ramklasser, till exempelBitmap.

    • Shallow Size:

    • Skall storlek: Total mängd Java-minne som används av den här objekttypen (i byte).

    • Retained Size: Storlek som behålls:

    Du kan använda de två menyerna ovanför listan över allokerade objekt för att välja vilkaheapdumpar som ska inspekteras och hur uppgifterna ska organiseras.

    I menyn till vänster väljer du vilken heap som ska inspekteras:

    • standardheap: När ingen heap anges av systemet.
    • app heap:
    • image heap: Den primära heap på vilken din app allokerar minne: Systemets uppstartsavbild, som innehåller klasser som laddas in under uppstarten. Allokeringar här kommer garanterat aldrig att flyttas eller försvinna.
    • zygote heap: I menyn till höger väljer du hur allokeringarna ska ordnas:
      • Ordna efter klass: Grupperar alla tilldelningar baserat på klassnamn. Detta är standard.
      • Ordna efter paket:
      • Ordna efter callstack: Grupperar alla allokeringar i motsvarande anropsstack. Det här alternativet fungerar endast om du fångar heapdumpen när du registrerar allokeringar. Trots detta finns det sannolikt objekt i heap som allokerades innan du började spela in, så dessa allokeringar visas först,helt enkelt listade efter klassnamn.

      Listan är som standard sorterad efter kolumnen Retained Size (bibehållen storlek). Om du vill sortera efter värdena i en annan kolumn klickar du på kolumnens rubrik.

      Klicka på ett klassnamn för att öppna fönstret Instance View till höger (visas i figur 6). Varje listad instans innehåller följande:

      • Djup: Det kortaste antalet hopp från en GC-rot till den valda instansen.
      • Native Size: Native Size: Native Size: Nate Size: Native Size: Native Size: Native Size: Native Size: Native Size: Native Size: Den här kolumnen är endast synlig för Android 7.0 och högre.
      • Shallow Size: Storlek på den här instansen i det ursprungliga minnet: Storlek för den här instansen i Javaminnet.
      • Retained Size: Storlek på det minne som den här instansen dominerar (enligt dominatorträdet).

      Figur 6. Den tid som krävs för att fånga en heapdump anges i tidslinjen

      För att inspektera din heap följer du dessa steg:

      1. Bläddra i listan för att hitta objekt som har ovanligt stora heapantal och som kan vara läckta. Om du vill hitta kända klasser kan du klicka på kolumnrubriken Klassnamn för att sortera i alfabetisk ordning. Klicka sedan på ett klassnamn. FönstretInstance View visas till höger och visar varje instans av den klassen, enligt figur 6.
        • Alternativt kan du hitta objekt snabbt genom att klicka på Filter, eller genom att trycka på Control+F (Command+F på Mac) och skriva in ett klass- eller paketnamn i sökfältet. Du kan också söka efter metodnamn om du väljerArrange by callstack från rullgardinsmenyn. Om du vill använda reguljära uttryck kryssar du i rutan bredvid Regex. Markera rutan bredvidMatch case om din sökfråga är skiftlägeskänslig.
      2. I fönstret Instance View klickar du på en instans. Referenstabellen visas nedan och visar alla referenser till det objektet.

        Och klicka på pilen bredvid instansnamnet för att visa alla dess fält och klicka sedan på ett fältnamn för att visa alla dess referenser. Om du vill visa instansinformationen för ett fält högerklickar du på fältet och väljer Gå till instans.

      3. Om du identifierar en referens på fliken Referenser som kan tappa minnet högerklickar du på den och väljer Gå till instans. Detta väljer motsvarande instans från heapdump och visar dess egna instansdata.

      I din heapdump letar du efter minnesläckage som orsakas av något av följande:

      • Långvariga referenser till Activity, Context,View, Drawable och andra objekt som kan ha en referens till Activity– eller Context-behållaren.
      • Non-statiska inre klasser, t.ex. en Runnable, som kan hålla en Activity-instans.
      • Cacher som håller objekt längre än nödvändigt.

      Spara en heapdump som en HPROF-fil

      När du har fångat en heapdump kan data endast visas i Memory Profiler under tiden som profilern körs. När du avslutar profileringssessionen förlorar du heapdumpen. Så om du vill spara den för granskning senare exporterar du heapdumpen till en HPROF-fil. I Android Studio 3.1 och lägre finns knappen Export capture to file på vänster sida av verktygsfältet under tidslinjen; iAndroid Studio 3.2 och högre finns knappen Export Heap Dump till höger om varje Heap Dump-post i fönstret Sessions. I dialogen Export As som visas sparar du filen med filnamnstillägget .hprof.

      För att använda en annan HPROF-analysator somjhat måste du konvertera HPROF-filen från Android-formatet till Java SE HPROF-formatet.Du kan göra det med verktyget hprof-conv som finns i katalogenandroid_sdk/platform-tools/. Kör hprof-convkommandot med två argument: den ursprungliga HPROF-filen och platsen för att skriva den konverterade HPROF-filen. Till exempel:

      hprof-conv heap-original.hprof heap-converted.hprof

      Import a heap dump file

      För att importera en HPROF-fil (.hprof) klickar du på Starta en ny profileringssession i fönstretSessions, väljer Ladda från fil och väljer filen från filebrowsern.

      Du kan också importera en HPROF-fil genom att dra den från filebrowsern till etteditorfönster.

      Detektering av läckor i Memory Profiler

      När du analyserar en heapdump i Memory Profiler kan du filtrera profileringsdata som Android Studio tror kan indikera minnesläckor för Activity och Fragment instanser i din app.

      De typer av data som filtret visar är bland annat följande:

      • Activity instanser som har förstörts men som fortfarande refereras.
      • Fragment instanser som inte har en giltig FragmentManager men som fortfarande refereras.

      I vissa situationer, t.ex. följande, kan filtret ge falska positiva resultat:

      • En Fragment är skapad men har ännu inte använts.
      • En Fragment cachelagras men inte som en del av en FragmentTransaction.

      För att kunna använda den här funktionen måste du först fånga upp en heapdumpfil i Android Studio. Om du vill visa de fragment och aktiviteter som kanske läcker minne markerar du kryssrutan Activity/Fragment Leaks i fönstret heapdump i Memory Profiler, vilket visas i figur 7.

      Figur 7. Filtrering av en heapdump för minnesläckor.

      Tekniker för profilering av ditt minne

      När du använder Memory Profiler bör du stressa din applikationskod och försöka leta efter minnesläckor. Ett sätt att framkalla minnesläckor i din app är att låta den köras ett tag innan du inspekterar Heap. Läckor kan sippra upp till toppen av allokeringarna i heap. Ju mindre läckan är, desto längre måste du köra appen för att se den.

      Du kan också utlösa en minnesläcka på något av följande sätt:

      • Rota enheten från stående till liggande och tillbaka igen flera gånger i olika aktivitetstillstånd. Att rotera enheten kan ofta leda till att en app läcker ett Activity, Context eller View-objekt eftersom systemet skapar Activity och om din app har en referens till ett av dessa objekt någon annanstans kan systemet inte samla in det.
      • Växla mellan din app och en annan app medan de befinner sig i olika aktivitetstillstånd (navigera till hemskärmen och återgå sedan till din app).

      Tip: Du kan också utföra ovanstående steg genom att använda themonkeyrunner testframework.

Lämna ett svar

Din e-postadress kommer inte publiceras.