Memory Leaks and Bloat — What It Is, Causes and How to Avoid

Author: IT Sectr Published: 2026-07-28 Reading time: 10 min

Memory leak — one of the most insidious problems in mobile development. The app’s memory usage steadily grows until it reaches the limit set by the OS, followed by an OutOfMemoryError or forced termination. According to Square Engineering, about 40% of Android apps have at least one memory leak that can only be detected through profiling. Let’s examine the causes and methods for preventing memory growth.

Key Takeaways

  • GC reachability — an object is not collected if there is an active reference from the root set
  • Static references to Activity or Context — the most common cause of leaks in Android
  • LeakCanary — the standard tool for automatic leak detection in Android
  • WeakReference — a solution for references that should not prevent garbage collection
  • Lifecycle-aware components automatically cancel subscriptions when the view is destroyed

What Is a Memory Leak and App Bloat?

Memory leak — a situation where an object that is no longer needed by the app continues to be held in the heap because an active reference from the GC Root set still points to it. The garbage collector considers such an object alive and does not remove it.

Memory bloat — a broader problem where the app consumes more memory than necessary to perform its current tasks. Causes: excessive caching, object duplication, suboptimal data structures, and heap fragmentation.

In Android, each app is allocated a limited heap (typically 64–512 MB depending on the device and OS version). In iOS, the limit is less strict, but the system sends a memory warning when approaching the threshold.

CharacteristicAndroidiOS
Heap limit64–512 MB (depends on device)Implicit (system)
Garbage collectionART (Concurrent, Compact)ARC (Automatic Reference Counting)
Leak mechanismGC Root referencesRetain cycles (strong reference cycles)
OutcomeOutOfMemoryErrorMemory warning → termination

According to the Facebook Engineering Blog, memory leaks cause ~15% of crash reports in mobile apps. On Android, this is compounded by ANRs due to frequent GC pauses when memory is low.

Common Memory Leak Patterns in Android and iOS

Static reference to Activity — a classic Android leak. If a static field or singleton holds a reference to an Activity, it will not be GC-collected even after finish() as long as the singleton is alive. An Activity is a heavy object containing a View hierarchy, resources, and Context.

kotlin
object LeakHolder {
    var activityRef: Activity ?= null // leak: static reference to Activity
}

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle ?= null) {
        super.onCreate(savedInstanceState)
        LeakHolder.activityRef = this // ❌ MainActivity will never be GC'd
    }
}

Anonymous classes and lambdas — implicitly hold a reference to the outer class. If a Runnable or Callback is passed to an external service and the Activity is destroyed, the anonymous class object still sits in the queue and prevents the Activity from being garbage collected.

  • Handler with a delay — if the Activity is destroyed but Handler.postDelayed has not yet executed, the Activity leaks
  • Thread and AsyncTask — on screen rotation, the Activity is recreated while the old Thread continues holding a reference to the old Activity
  • Retrofit/Callback — an anonymous Callback holds a reference to the presenter or fragment
  • Observers — LiveData or RxJava subscriptions without unsubscription on onDestroy

In iOS, the main problem is retain cycles: two objects hold strong references to each other, and ARC cannot zero out the reference count for either. A typical case: a closure that captures self strongly, and self that holds a reference to the closure.

How to Detect Memory Leaks?

LeakCanary — a library from Square for automatic leak detection in Android. After an Activity or Fragment is destroyed, it checks whether the object was GC-collected. If not, it takes a heap dump and shows the leak trace.

kotlin
// LeakCanary 2.x — auto-integration via Application
class ExampleApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        // LeakCanary auto-installs in debug build
        // via ContentProvider — zero code setup
    }
}

// Force check invocation
AppWatcher.objectWatcher.watch(watchedObject, "leak description")

Android Studio Profiler — a built-in tool for real-time memory monitoring. It allows you to record a heap dump, find suspicious objects (Retained Size > 1 MB), and trace the GC root path to each object.

For iOS, use the Xcode Memory Graph Debugger. It visualizes the object graph in memory, shows retain cycles, and lets you instantly detect circular references. Instruments > Allocations is also available for long-term monitoring.

Prevention Strategies

WeakReference — a basic mechanism for references that should not interfere with garbage collection. If the GC decides to collect an object, WeakReference returns null. It is used for callbacks, listeners, and references to UI components from background threads.

Lifecycle-aware components — an architectural approach implemented in Android Jetpack (Lifecycle, LiveData, Flow, coroutines). Subscriptions are automatically cancelled on onDestroy, eliminating the main class of leaks.

kotlin
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)
            // coroutine auto-cancels on onCleared()
        }
    }
}

viewModelScope and lifecycleScope — built-in CoroutineScope in Android that are cancelled on the corresponding lifecycle event. This eliminates leaks through coroutines — the most common scenario in modern Android development.

  • Do not use static references to Context, Activity, View, or Fragment
  • Cancel all RxJava subscriptions in disposeBag / CompositeDisposable on onDestroy
  • Use [weak self] / [unowned self] in iOS closures to prevent retain cycles
  • Check Bitmap and large objects — they should be recycled or nullified

Memory Profiling Tools

Memory Profiler in Android Studio — the primary tool for heap monitoring. It shows live allocations, heap snapshots, and object counts by type. It allows you to record a dump and analyze it in MAT (Memory Analyzer Tool) to find suspicious objects.

Eclipse MAT — a desktop heap dump analyzer. After loading an HPROF file from Android Studio, MAT builds a dominator tree, shows the retain size of each object, and offers automatic leak suspect analysis through the Leak Suspects Report.

Xcode Memory Graph — a visual retain cycle debugger. When you click the Memory Graph Debugger button, Xcode stops the app, builds a complete object graph in memory, and highlights retain cycles in red.

ToolPlatformFeature
LeakCanaryAndroidAuto-detection of leaks after destroy
Memory ProfilerAndroid StudioHeap dump + live allocations
Eclipse MATAndroidDominator tree, Leak Suspects Report
Memory GraphiOS (Xcode)Retain cycles visualiser

According to Google I/O 2023, apps using LeakCanary in debug builds reduce memory-related crashes by 30–50% in the first 2 months after adoption. It is recommended to add LeakCanary during the project onboarding stage.

Frequently Asked Questions

How is a memory leak different from bloat?

Leak — objects that are unreachable by code but not collected by GC due to active references. Bloat — the app holds objects that are logically needed but in excessive quantity (e.g., a 50 MB cache in an 80 MB running app). Bloat is fixed architecturally; leaks are fixed through correct reference management.

How does LeakCanary find leaks?

LeakCanary uses ObjectWatcher — after onDestroy() of an Activity, it creates a WeakReference to the Activity and triggers GC. If the WeakReference is not cleared after 5 seconds, LeakCanary takes a heap dump, analyzes the shortest reference chain from the GC Root to the object, and shows the exact leak stack with the file and line number.

Why does Bitmap often cause OutOfMemoryError?

Bitmap occupies memory outside the Java heap in native memory (native heap). The size of one Bitmap = width × height × 4 bytes (ARGB_8888). A 12 MP photo (4000×3000) takes 48 MB. Android cannot always free native memory promptly, so accumulating several Bitmaps leads to OOM even with sufficient Java heap.

What is a retain cycle in iOS?

Retain cycle — a situation in ARC where two objects hold strong references to each other, and the reference count never reaches zero. A typical example: a ViewController with a strong reference to a closure, and the closure captures self strongly. Solution: use [weak self] or [unowned self] in closures.

What is the maximum heap size on Android?

The heap size depends on the device and Android version. For older devices (API 15–24) — 64–128 MB. For modern ones (API 25+) — 256–512 MB. The exact value can be obtained via ActivityManager.getMemoryClass(). For large apps (games, editors), largeHeap=true in the manifest provides up to 1 GB.

Summary

  • Memory leak — an object not collected by GC due to an active reference from the root set; bloat — excessive memory consumption without explicit leaks
  • Static references to Activity, Context, or View — the number one cause of leaks in Android; solution — WeakReference or Application Context
  • Anonymous classes and lambdas implicitly hold a reference to the outer class; un-cancelled callbacks are the second most common cause
  • LeakCanary — the standard for auto-detection of leaks in Android; integration takes 5 minutes and reduces crash rate by 30–50%
  • lifecycleScope and viewModelScope automatically cancel coroutines on destroy, eliminating a whole class of leaks
  • Retain cycles in iOS are solved with weak/unowned self in closures and delegates
  • Profile memory at least once per sprint — a heap dump with MAT or Memory Graph should become part of code review

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also