Minnesläcka — ett av de lömskaste problemen inom mobilutveckling. Appens minne växer oavbrutet tills det når gränsen som satts av operativsystemet, varefter OutOfMemoryError eller tvungen avslutning följer. Enligt Square Engineering har cirka 40% av Android-appar minst en minnesläcka som bara kan upptäckas vid profilering. Låt oss gå igenom orsakerna och metoderna för att förhindra minnestillväxt.
Huvudpunkter
Minnesläcka (memory leak) — en situation där ett objekt som appen inte längre behöver fortsätter att finnas kvar i heapen eftersom det finns en aktiv referens från rotuppsättningen (GC Root). Soprensaren betraktar ett sådant objekt som levande och tar inte bort det.
Minnesvällning (memory bloat) — ett bredare problem när appen förbrukar mer minne än nödvändigt för att utföra aktuella uppgifter. Orsaker: överdriven cachning, duplicering av objekt, suboptimala datastrukturer och fragmentering av heapen.
I Android tilldelas varje app en begränsad heap (vanligtvis 64-512 MB beroende på enhet och OS-version). I iOS är begränsningen mindre strikt, men systemet skickar en minnesvarning när gränsen närmar sig.
| Egenskap | Android | iOS |
|---|---|---|
| Heap-gräns | 64-512 MB (beroende på enhet) | Implicit (system) |
| Sophämtning | ART (Concurrent, Compact) | ARC (Automatic Reference Counting) |
| Läckagemekanism | GC Root-referenser | Retain-cykler (starka referenscykler) |
| Resultat | OutOfMemoryError | Minnesvarning → avslutning |
Enligt Facebook Engineering Blog är minnesläckor orsaken till ~15% av crash-rapporterna i mobila appar. I Android tillkommer ANR på grund av täta GC-pauser vid minnesbrist.
Statisk referens till Activity — klassikern bland Android-läckor. Om ett statiskt fält eller en singleton lagrar en referens till en Activity, kommer den inte att samlas in av GC ens efter finish(), så länge singletonen lever. Activity är ett tungt objekt som innehåller View-hierarki, resurser och Context.
object LeakHolder {
var activityRef: Activity ?= null // läcka: statisk referens till Activity
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle ?= null) {
super.onCreate(savedInstanceState)
LeakHolder.activityRef = this // ❌ MainActivity kommer aldrig att samlas in av GC
}
}
Anonyma klasser och lambdas — håller implicit en referens till den yttre klassen. Om en Runnable eller Callback skickas till en extern tjänst och Activity förstörs, hänger det anonyma klassobjektet fortfarande i kön och hindrar Activity från att samlas in av GC.
I iOS är huvudproblemet retain-cykler: två objekt håller starka referenser till varandra och ARC kan inte nollställa referensräknaren för något av dem. Typiskt fall: en closure som starkt fångar self, och self som håller en referens till closuren.
LeakCanary — ett bibliotek från Square för automatisk läckagedetektering i Android. Efter att en Activity eller Fragment har förstörts kontrollerar det om objektet har samlats in av GC. Om inte — gör det en heap dump och visar läckagespåret.
// LeakCanary 2.x — auto-integration via Application
class ExampleApplication : Application() {
override fun onCreate() {
super.onCreate()
// LeakCanary installerar sig automatiskt i debug-bygge
// via ContentProvider — noll kodkonfiguration
}
}
// Tvinga kontrollanrop
AppWatcher.objectWatcher.watch(watchedObject, "leak description")
Android Studio Profiler — det inbyggda verktyget för minnesövervakning i realtid. Gör det möjligt att spela in heap dump, hitta misstänkta objekt (Retained Size > 1 MB) och spåra GC-rootvägen till varje objekt.
För iOS använder du Xcode Memory Graph Debugger. Den visualiserar objektgrafen i minnet, visar retain-cykler och möjliggör omedelbar upptäckt av cirkulära referenser. Även Instruments > Allocations finns tillgängligt för långsiktig övervakning.
WeakReference — den grundläggande mekanismen för referenser som inte ska hindra sophämtning. Om GC bestämmer sig för att ta bort objektet returnerar WeakReference null. Används för callbacks, listeners och referenser till UI-komponenter från bakgrundstrådar.
Lifecycle-aware-komponenter — ett arkitekturellt tillvägagångssätt implementerat i Android Jetpack (Lifecycle, LiveData, Flow, coroutines). Prenumerationer avbryts automatiskt vid onDestroy, vilket eliminerar huvudklassen av läckor.
class MyViewModel : ViewModel() {
private val _data = MutableLiveData<List<User>>()
val data: LiveData<List<User>> get() = _data
fun loadData() {
viewModelScope.launch {
val result = repository.fetchData()
_data.postValue(result)
// korutin avbryts automatiskt vid onCleared()
}
}
}
viewModelScope och lifecycleScope — inbyggda CoroutineScope i Android som avbryts vid motsvarande livscykelhändelse. Detta eliminerar läckor genom korutiner — det vanligaste scenariot i modern Android-utveckling.
Memory Profiler in Android Studio — huvudverktyget för heap-övervakning. Visar live-alloceringar, heap-ögonblicksbilder, antal objekt per typ. Gör det möjligt att spela in en dump och analysera den i MAT (Memory Analyzer Tool) för att hitta misstänkta objekt.
Eclipse MAT — en stationär heap dump-analysator. Efter att ha laddat en HPROF-fil från Android Studio bygger MAT ett dominatorträd, visar retain-storleken för varje objekt och erbjuder automatisk analys av misstänkta läckor via Leak Suspects Report.
Xcode Memory Graph — en visuell felsökare för retain-cykler. När du klickar på Memory Graph Debugger-knappen stoppar Xcode appen, bygger en komplett objektgraf i minnet och markerar retain-cykler i rött.
| Verktyg | Plattform | Egenskap |
|---|---|---|
| LeakCanary | Android | Automatisk läckagedetektering efter destroy |
| Memory Profiler | Android Studio | Heap dump + live-alloceringar |
| Eclipse MAT | Android | Dominatorträd, Leak Suspects Report |
| Memory Graph | iOS (Xcode) | Retain-cycle-visualiserare |
Enligt Google I/O 2023 minskar appar som använder LeakCanary i debug-byggen antalet minnesrelaterade krascher med 30-50% under de första 2 månaderna efter implementering. Det rekommenderas att lägga till LeakCanary i onboarding-fasen av projektet.
Vanliga frågor
Läcka — objekt som inte är tillgängliga för koden men inte tas bort av GC på grund av aktiva referenser. Svällning — appen håller objekt i minnet som logiskt sett behövs men i överdriven mängd (t.ex. 50 MB cache i en app som väger 80 MB). Svällning åtgärdas arkitekturellt, läcka — genom korrekt referenshantering.
LeakCanary använder ObjectWatcher — efter onDestroy() av en Activity skapar den en WeakReference till Activity och startar GC. Om WeakReference inte är rensad efter 5 sekunder gör LeakCanary en heap dump, analyserar den kortaste referenskedjan från GC Root till objektet och visar den exakta läckagestacken med filnamn och kodrad.
Bitmap upptar minne utanför Java-heapen i det inbyggda minnet (native heap). Storleken på en Bitmap = bredd × höjd × 4 byte (ARGB_8888). Ett 12 MP-foto (4000×3000) upptar 48 MB. Android kan inte alltid frigöra inbyggt minne i tid, vilket vid ackumulering av flera Bitmaps leder till OOM även med tillräcklig Java-heap.
Retain-cycle — en situation i ARC där två objekt håller starka referenser till varandra och referensräknaren aldrig når noll. Typiskt exempel: ViewController med en stark referens till en closure, och closuren fångar self starkt. Lösning: använd [weak self] eller [unowned self] i closures.
Heap-storleken beror på enheten och Android-versionen. För gamla enheter (API 15-24) — 64-128 MB. För moderna (API 25+) — 256-512 MB. Det exakta värdet kan erhållas via ActivityManager.getMemoryClass(). För stora appar (spel, redigeringsprogram) finns largeHeap=true i manifestet, vilket ger upp till 1 GB.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också