Android

Android Memory Profiling: Complete Guide with Android Studio

In this guide, you'll learn to use the built-in Profiler in Android Studio to analyze memory usage, detect leaks, and optimize your Android app's performance.

Updated at February 15, 2026
15-20 min
Medium
FixPedia Team
Применимо к:Android Studio Hedgehog (2023.1.1)+Android 5.0 (API 21)+Kotlin 1.8+ / Java 11+

Introduction / Why This Matters

Memory profiling is a critical step in developing stable Android applications. Memory leaks lead to a gradual increase in RAM consumption, performance slowdowns, and in the worst case, crashes with an OutOfMemoryError. The built-in Memory Profiler in Android Studio allows you to visually track memory allocation in real-time, take heap dumps, and identify objects that shouldn't be in memory. This guide will walk you through the entire process, from launching the tool to interpreting the results.

Prerequisites / Preparation

Before you begin, ensure that:

  1. You have Android Studio version Hedgehog (2023.1.1) or newer installed. The tool is constantly improving.
  2. Your device or emulator is running Android 5.0 (API 21) or higher.
  3. Your app is built in a debug configuration (with debugging information enabled). Analysis of release builds is possible but more difficult.
  4. In your app's build.gradle (module), debuggable true is enabled for debug builds (this is typically the default).
  5. Your device is connected via USB with USB debugging enabled, or an emulator is running.

Step 1: Launch Profiler and Connect Your Device

  1. Run your app on a device or emulator from Android Studio (click the Run button).
  2. Open the Profiler window: View → Tool Windows → Profiler or use the shortcut Alt+Shift+F10 (Windows/Linux) / ⌥⇧F10 (macOS).
  3. At the top of the Profiler window, you'll see a list of active processes. Select your app's process (usually named with the package name, e.g., com.example.myapp).
  4. The CPU tab opens by default. Switch to the Memory tab (the icon with a blue graph and numbers).

On the graph, you'll see your app's real-time memory consumption:

  • Java Heap: Memory managed by the JVM/ART (your code).
  • Native Heap: Memory allocated via the NDK (C/C++ code).
  • Graphics: Memory used for rendering buffers.
  • Stack: Memory for stack frames.
  • Code: Memory for compiled code.
  • Others: Other memory.

Step 2: Start Recording and Perform a Scenario

  1. On the Memory tab's toolbar, click the Record memory allocations button (the icon with a red dot inside a circle). The recording status will change to Recording....
  2. Perform an action in your app that you suspect causes a leak. For example:
    • Open and close a fragment/activity several times.
    • Scroll a list (RecyclerView) with many items.
    • Navigate to another screen and back.
  3. Important: Try to reproduce a scenario a user might perform repeatedly. A leak often manifests after several iterations.
  4. After performing the action, click the Stop recording button (the same button, now red). Object allocation recording will stop.

💡 Tip: Don't record for too long—this can generate a huge amount of data and slow down performance. 30-60 seconds of active actions is usually sufficient.

Step 3: Take a Heap Dump for Analysis

Recording allocations is useful for tracking object creation frequency, but to find leaks you need a Heap Dump—a complete snapshot of objects in the heap at a specific moment.

  1. Ensure you are on the Memory tab.
  2. Click the Dump Java heap button (the icon with a heap of trash and a downward arrow). The profiler will request the system to create a dump.
  3. Wait for it to finish. A new Heap Dump tab will appear with the results.

Step 4: Analyze Results in the Analyzer

The Heap Dump tab automatically opens the analyzer. By default, you see the Classes table.

How to read the table:

  • Class — The object's class name.
  • Instance Count — The number of instances of this class in the heap at the time of the dump.
  • Size — The total memory occupied by all instances of this class (in KB/MB).
  • Retained SizeThe most important metric. This is the amount of memory that would be freed if all objects of this class (and those they reference) were deleted. A large Retained Size for a class that shouldn't be in memory is a clear sign of a leak.

First action: Sort the table by the Retained Size column (descending). The classes holding the largest amounts of memory will be at the top.

What to look for:

  • Classes from your app (e.g., MyActivity, MyAdapter, MyViewModel) that should not exist after a screen/fragment is closed.
  • Classes from libraries known for leaks (e.g., older versions of androidx.lifecycle.ViewModel with improper configuration).
  • A large number of Bitmap, Context, View, Activity, or Fragment instances.

Step 5: Investigate Paths to GC Roots

Found a suspicious class? Now you need to understand why it isn't being garbage collected. To do this, find the GC Root—the object that directly or through a chain of references holds your object in memory.

  1. In the Classes table, find the suspicious class and select it (click the row).
  2. On the right panel (or in the context menu), choose Show Path to GC Roots.
  3. A dialog will appear with options. Select Show all paths (or Show soft references if you suspect caches). Click OK.
  4. The analyzer will build a reference tree from the GC Root (at the top) to your object (at the bottom).

How to interpret:

  • The GC Root is typically a system object (e.g., android.app.ActivityThread, java.lang.Thread, android.view.ViewRootImpl) or a static field of your class (MyClass.sStaticField).
  • Look in the chain for an activity context (Context), which might be stored in a static field or a long-lived singleton. This is a classic activity leak.
  • If the chain includes your Singleton or an Object from a cache—it means it isn't cleared when the screen is destroyed.

⚠️ Important: Not all paths to a GC Root indicate a leak. If the object is genuinely needed for app functionality (e.g., a service), its presence is justified. Look for objects that should have been destroyed (closed activities, fragments).

Verifying the Fix

After making code changes (e.g., clearing references in onDestroy(), using WeakReference, replacing static contexts with ApplicationContext), repeat steps 1-4.

Success criteria:

  1. On the Memory Profiler graph, after closing the problematic screen, memory (Java Heap) returns to a baseline level instead of growing.
  2. In a new Heap Dump, the Instance Count of the suspicious class has significantly decreased or is zero.
  3. In the analyzer, the Retained Size for that class is now close to zero.
  4. The path to the GC Root for that class no longer exists or leads to an object that legitimately lives for the entire app lifecycle.

Common Issues

1. Profiler doesn't show your app's process

  • Cause: The app is built in a release configuration without the debuggable flag, or the device isn't authorized for debugging.
  • Solution: Run a debug build. On the device, in Settings → Developer options, ensure USB debugging is enabled and you have confirmed the computer's RSA key.

2. Heap Dump takes very long or fails with an error

  • Cause: Too many objects in the heap (tens of thousands), or insufficient memory on your computer for analysis.
  • Solution:
    • Simplify the scenario; take the dump earlier.
    • Close other heavy apps in the emulator/device.
    • Increase the RAM allocated to the emulator (in AVD Manager).
    • Use filters in the analyzer (the Filter field) to view only your packages (e.g., com.example).

3. Can't find the problematic class—only system classes are listed

  • Cause: The leak might be in native code (Native Heap) or in objects the Profiler cannot display correctly (e.g., due to transparent wrappers).
  • Solution:
    • Switch to the Native tab in Profiler and check native heap growth.
    • Use adb shell dumpsys meminfo <package_name> in the terminal for a lower-level report.
    • If you use libraries with native code (e.g., OpenGL, FFmpeg), check their documentation for known leaks.

4. Memory graph still grows after fixing the leak

  • Cause: You have more than one leak, or the growth is due to legitimate caching (e.g., an LruCache).
  • Solution:
    • Compare several Heap Dumps taken at intervals. See which classes increase in count between dumps.
    • Check if the growth is linear and predictable (e.g., adding items to an ArrayList without clearing). This might be business logic, not a leak.
    • Analyze the Allocation Tracker (the Record allocations button) to see where new objects are being created.

5. Path to GC Root goes through a system class I don't control

  • Cause: Some system classes (e.g., InputMethodManager) historically have leaks via uncleared references.
  • Solution:
    • Check if you are holding a reference to a View or Context in a static field or long-lived object yourself.
    • For known system leaks, there are established patterns: e.g., in an activity's onDestroy(), call inputMethodManager.isActive = false or clear focus from EditText.
    • Search for the specific case (class name + "memory leak") in the Android documentation or on Stack Overflow.

F.A.Q.

How often should you take Heap Dumps to find leaks?
Can you profile memory on a release build?
Difference between Memory Profiler and Allocation Tracker?
Why doesn't memory drop to zero after GC?

Hints

Launch Profiler and connect device
Start recording and perform the scenario
Take a Heap Dump for analysis
Analyze results in Analyzer
Investigate paths to GC Root
FixPedia

Free encyclopedia for fixing errors. Step-by-step guides for Windows, Linux, macOS and more.

© 2026 FixPedia. All materials are available for free.

Made with for the community