Memory management best practices

This doc assumes you have followed best-practice guidance for Android apps under memory management, such as Manage your app's memory.

Introduction

A memory leak is a type of resource leak that occurs when a computer program does not release allocated memory that is no longer needed. A leak can lead to the application requesting more memory from the OS than it has available, and thus crashing the application. A number of improper practices can cause memory leaks in Android apps, such as not properly disposing of resources or not unregistering listeners when no longer needed.

This document provides you with some best practices to help prevent, detect, and resolve memory leaks in your code. If you have tried the methods in this document and suspect a memory leak in our SDKs, see How to report issues with Google SDKs.

Before you contact support

Before you report a memory leak to the Google support team, follow the best practices along with the debugging steps provided in this document to ensure the error is not in your code. These steps may resolve your issue, and if they don't, they generate the information the Google support team needs to help you.

Prevent memory leaks

Follow these best practices to help avoid some of the most common causes for memory leaks in code that uses Google SDKs.

Best practices for Android apps

Check that you have done all of the following in your Android application:

  1. Release unused resources.
  2. Unregister listeners when no longer needed.
  3. Cancel tasks when not needed.
  4. Forward lifecycle methods to release resources.
  5. Use the latest versions of the SDKs

For specific details for each of these practices, see the following sections.

Release unused resources

When your Android app uses a resource, be sure to release the resource when it is no longer needed. If you don't, the resource continues to take up memory even after your application finishes with them. For more information, review The activity lifecycle in the Android documentation.

Release stale GoogleMap references in GeoSDKs

A common mistake is that a GoogleMap can cause a memory leak if cached using NavigationView or MapView. A GoogleMap has a 1 to 1 relationship with the NavigationView or MapView from which it is retrieved. You must either ensure that a GoogleMap is not cached, or that the reference is released when NavigationView#onDestroy or MapView#onDestroy is called. If using the NavigationSupportFragment, MapSupportFragment, or your own fragment wrapping these views, then the reference must be released in Fragment#onDestroyView.

class NavFragment : SupportNavigationFragment() {

  var googleMap: GoogleMap?

  override fun onCreateView(
    inflater: LayoutInflater,
    parent: ViewGroup?,
    savedInstanceState: Bundle?,
  ): View  {
    super.onCreateView(inflater,parent,savedInstanceState)
    getMapAsync{map -> googleMap = map}
  }

  override fun onDestroyView() {
    googleMap = null
  }
}

Unregister listeners when no longer needed

When your Android app registers a listener for an event, such as a button click or a change in the state of a view, be sure to unregister the listener when the application no longer needs to monitor the event. If you don't, the listeners continue to take up memory even after your application finishes with them.

For example, suppose your application uses the Navigation SDK and it calls the following listener to listen for arrival events: addArrivalListener method to listen for arrival events, it should also call removeArrivalListener when it no longer needs to monitor the arrival events.

var arrivalListener: Navigator.ArrivalListener? = null

fun registerNavigationListeners() {
  arrivalListener =
    Navigator.ArrivalListener {
      ...
    }
  navigator.addArrivalListener(arrivalListener)
}

override fun onDestroy() {
  navView.onDestroy()
  if (arrivalListener != null) {
    navigator.removeArrivalListener(arrivalListener)
  }

  ...
  super.onDestroy()
}

Cancel tasks when not needed

When an Android app starts an asynchronous task, such as a download or a network request, make sure you cancel the task when it finishes. If the task is not canceled, it continues to run in the background even after the app has finished with it.

For more details on the best practices, see Manage your app's memory in the Android documentation.

Forward lifecycle methods to release resources

If your app uses the Navigation or Maps SDK, make sure to release the resources by forwarding lifecycle methods (shown in bold) to navView. You can do this using NavigationView in the Navigation SDK or MapView in the Maps or Navigation SDK. You may also use SupportNavigationFragment or SupportMapFragment instead of directly using NavigationView and MapView, respectively. The support fragments handle the forwarding of the lifecycle methods.

class NavViewActivity : AppCompatActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    ...
    navView = ...
    navView.onCreate(savedInstanceState)
    ...
  }

  override fun onSaveInstanceState(savedInstanceState: Bundle) {
    super.onSaveInstanceState(savedInstanceState)
    navView.onSaveInstanceState(savedInstanceState)
  }

  override fun onTrimMemory(level: Int) {
    super.onTrimMemory(level)
    navView.onTrimMemory(level)
  }

  /* Same with
    override fun onStart()
    override fun onResume()
    override fun onPause()
    override fun onConfigurationChanged(...)
    override fun onStop()
    override fun onDestroy()
  */
}

Use the latest versions of the SDKs

Google SDKs are constantly being updated with new features, bug fixes, and performance improvements. Keep the SDKs in your app up-to-date to receive these fixes.

Debug memory leaks

If you still see memory leaks after implementing all the applicable suggestions earlier in this document, follow this process to debug.

Before you get started, you should be familiar with how Android manages memory. For information, read the Android Overview of memory management.

To debug memory leaks, follow this process:

  1. Recreate the issue. This step is essential to debugging it.
  2. Check if the memory usage is expected. Check that the increased usage that appears to be a leak is not actually the memory required to run your application.
  3. Debug at a high-level. There are several utilities you can use to debug. Three different standard tool sets help debug memory issues in Android: Android Studio, Perfetto, and the Android Debug Bridge (adb) command line utilities.
  4. Check your app's memory use. Get a heap dump and allocation tracking and then analyze it.
  5. Fix memory leaks.

The following sections cover these steps in detail.

Step 1: Recreate the issue

If you have not been able to recreate the problem, first consider the scenarios that could lead to the memory leak. Jumping straight into looking at a heap dump might work, if you know the issue has been recreated. However, if you just get a heap dump on app startup or another random point in time, then you might not have activated the conditions to trigger a leak. Consider working through various scenarios when trying to recreate the issue:

  • What set of features are activated?

  • What specific sequence of user actions triggers the leak?

    • Have you tried multiple iterations of activating this sequence?
  • Which lifecycle states has the app cycled through?

    • Have you tried multiple iterations through different lifecycle states?

Make sure that you can recreate the issue in the latest version of the SDKs. The issue from a previous version may have already been fixed.

Step 2: Check if the memory usage for the app is expected

Every feature requires additional memory. When you debug different scenarios, consider whether or not this could be expected usage or if it actually is a memory leak. For example, for different features or user tasks, consider the following possibilities:

  • Likely a leak: Activating the scenario through multiple iterations results in an increase of memory usage over time.

  • Likely expected memory usage: Memory is reclaimed after the scenario is stopped.

  • Possibly expected memory usage: Memory usage increases for a period of time then tapers off. This could be due to a bounded cache or other expected memory usage.

If the app behavior is likely expected memory usage, the issue can be addressed by managing your app's memory. For help, see Manage your app's memory.

Step 3: Debug at a high level

When you debug a memory leak, start at a high level, and then drill down once you've narrowed down the possibilities. Use one of these high-level debugging tools to first analyze if there is a leak over time:

Android Studio Memory Profiler

This tool gives you a visual histogram of the memory consumed. Heap dumps and allocation tracking can also be triggered from this same interface. This tool is the default recommendation. For more information, see Android Studio Memory Profiler.

Perfetto Memory Counters

Perfetto gives you precise control over tracking several metrics and presents it all in a single histogram. For more information, see Perfetto Memory Counters.

Perfetto user interface

Android debug bridge (adb) command line utilities

Much of what you can track with Perfetto is also available as an adb command line utility that you can query directly. A couple of important examples are:

  • Meminfo lets you see detailed memory information at a point in time.

  • Procstats provides some important aggregated stats over time.

A crucial statistic to look at here is the maximum physical memory footprint (maxRSS) that the app requires over time. MaxPSS may not be as accurate. For a way to increase accuracy, see the adb shell dumpsys procstats --help –start-testing flag.

Allocation tracking

Allocation tracking identifies the stack trace where memory was allocated and if it was not freed. This step is especially useful when tracking down leaks in native code. Since this tool identifies the stack trace, it can be a great means to quickly debug the root cause or to figure out how to recreate the problem. For steps to use allocation tracking, see Debug memory in native code with allocation tracking.

Step 4: Check your app's memory use with a heap dump

One way to detect a memory leak is to get a heap dump of your app and then inspect it for leaks. A heap dump is a snapshot of all the objects in an app's memory. It can be used to diagnose memory leaks and other memory-related problems.

Android Studio can detect memory leaks not fixable by the GC. When you capture a heap dump, Android Studio checks whether there is an activity or fragment that is still reachable but has already been destroyed.

  1. Capture a heap dump.
  2. Analyze the heap dump to find memory leaks.
  3. Fix memory leaks.

For details, see the following sections.

Capture a heap dump

To capture a heap dump, you can use the Android Debug Bridge (adb) or the Android Studio Memory Profiler.

Use adb to capture a heap dump

To capture a heap dump using adb, follow these steps:

  1. Connect your Android device to your computer.
  2. Open a command prompt and navigate to the directory where the adb tools are.
  3. To capture a heap dump, run this command :

    adb shell am dumpheap my.app.name $PHONE_FILE_OUT

  4. To retrieve the heap dump, run this command:

    adb pull $PHONE_FILE_OUT $LOCAL_FILE.

Use Android Studio to capture a heap dump

To capture a heap dump using the Android Studio Memory Profiler, follow these steps in the Android Capture a heapdump section.

Analyze the heap dump to find memory leaks

Once you have captured a heap dump, you can use the Android Studio Memory Profiler to analyze it. To do this, follow these steps:

  1. Open your Android project in Android Studio.

  2. Select Run, and then select the Debug configuration.

  3. Open the Android Profiler tab.

  4. Select Memory.

  5. Select Open heap dump and select the heap dump file that you generated. The memory profiler displays a graph of your app's memory usage.

  6. Use the graph to analyze the heap dump:

    • Identify objects that are no longer being used.

    • Identify objects that use a lot of memory.

    • See how much memory each object is using.

  7. Use this information to narrow down or find the source of the memory leak and fix it.

Step 5: Fix memory leaks

Once you have identified the source of the memory leak, you can fix it. Fixing memory leaks in your Android apps helps improve the performance and stability of your apps. Depending on the scenario, the details vary. However, the following suggestions can help:

Other debugging tools

After these steps are complete, if you have still not found and fixed the memory leak, try these tools:

Debug memory in native code with allocation tracking

Even if you are not directly using native code, several common Android libraries do, including Google SDKs. If you think your memory leak is in native code, then there are several tools you can use to debug it. Allocation tracking with either Android Studio or heapprofd (also compatible with Perfetto) is a great way to identify potential causes of a memory leak and is often the quickest way to debug.

Allocation tracking also has the distinct advantage of letting you share the results without including sensitive information that can be found in a heap.

Identify leaks with LeakCanary

LeakCanary is a powerful tool for identifying memory leaks in Android apps. To learn more about how to use LeakCanary in your app, visit LeakCanary.

How to report issues with Google SDKs

If you have tried the methods in this document and suspect a memory leak in our SDKs, contact customer support with as much of the following information as possible:

  • Steps to recreate the memory leak. If the steps require complex coding, it may help to copy the code that replicates the issue into our sample app and provide additional steps that need to be taken in the UI to trigger the leak.

  • Heap dumps captured from your app with the issue recreated. Capture heap dumps at two different points in time that show that the memory usage has increased by a substantial amount.

  • If a native memory leak is expected, share the allocation tracking output from heapprofd.

  • A bug report taken after you have recreated the leak condition.

  • Stack traces of any memory-related crashes.

    Important note: Stack traces are usually not enough by themselves to debug a memory issue, so make sure you also provide one of the other forms of information.