Android Dev Summit, October 23-24: two days of technical content, directly from the Android team. Sign-up for livestream updates.

Manage memory effectively in games

On the Android platform, the system tries to use as much system memory (RAM) as possible and performs various memory optimizations to free up space when needed. These optimizations can have a negative effect on your game, either by slowing it down or killing it altogether. You can learn more about these optimizations in the topic Memory allocation among processes.

This page explains the steps you can take to avoid low memory conditions affecting your game.

Respond to onTrimMemory()

The system uses onTrimMemory() to notify your app that memory is running low and the app may be killed. Many times, this is the only warning your app gets. This callback has high latency relative to the low-memory killer (LMK), so it is critical to respond quickly to the callback.

In response to this callback, reduce the speed, number, and size of allocations. onTrimMemory() passes a constant indicating the severity, but you should respond to the first warning as it’s possible to allocate more quickly than what onTrimMemory() can react to.

Kotlin

class MainActivity : AppCompatActivity(), ComponentCallbacks2 {
    override fun onTrimMemory(level: Int) {
        when (level) {
            ComponentCallbacks2.TRIM_MEMORY_MODERATE,
                ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW,
                ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL -> // Respond to low memory condition
            else -> Unit
        }
    }
}

Java

public class MainActivity extends AppCompatActivity implements ComponentCallbacks2 {
    public void onTrimMemory(int level) {
        switch (level) {
            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
              // Respond to low memory condition
                break;
            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
              // Respond to low memory condition
                break;
            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
              // Respond to low memory condition
                break;
            default:
                break;

C#

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

class LowMemoryTrigger : MonoBehaviour
{
    private void Start()
    {
        Application.lowMemory += OnLowMemory;
    }
    private void OnLowMemory()
    {
        // Respond to low memory condition (e.g., Resources.UnloadUnusedAssets())
    }
}

Be conservative with memory budgets

Budget memory conservatively to avoid running out of memory. Some items to consider include the following:

  • Size of physical RAM: Games often use between ¼ and ½ of the physical RAM amount on the device.
  • Maximum zRAM size: More zRAM means the game potentially has more memory to allocate. This amount can vary based on device; look for SwapTotal in /proc/meminfo to find this value.
  • Memory usage of the OS: Devices that designate more RAM to system processes leave less memory for your game. The system kills your game's process before it kills system processes.
  • Memory usage of installed apps: Test your game on devices that have many apps installed. Social media and chat apps need to run constantly and affect the amount of free memory.

If you can’t commit to a conservative memory budget, take a more flexible approach. If the system runs into low memory issues, reduce the amount of memory that the game is using. For example, allocate lower-resolution textures or store fewer shaders in response to onTrimMemory(). This dynamic approach to memory allocation requires more work from the developer, especially in the game design phase.

Avoid thrashing

Thrashing occurs when free memory is low, but not low enough to kill the game. In this situation, kswapd has reclaimed pages that the game still needs, so it tries to reload the pages from memory. There isn’t enough space, so the pages keep getting swapped out (continuous swapping). System tracing reports this situation as a thread where kswapd runs continuously.

One symptom of thrashing is long frame times - possibly a second or more. Reduce the memory footprint of the game to resolve this situation.

Use available tools

Android has a collection of tools to assist in understanding how the system manages memory.

Meminfo

This tool collects memory statistics to show how much PSS memory was allocated and the categories for which it was used.

Print the meminfo statistics in one of the following ways:

  • Use the command adb shell dumpsys meminfo package-name.
  • Use the MemoryInfo call from the Android Debug API.

Memory tracepoints

Memory tracepoints track the amount of RSS memory your game is using. Calculating RSS memory usage is much faster than calculating PSS usage. Because it’s faster to calculate, RSS shows finer granularity on changes in the memory size for more accurate measurements of peak memory usage. Therefore, it's easier to notice peaks that could cause the game to run out of memory.

Perfetto and long traces

Perfetto is a suite of tools for collecting performance and memory information on a device and displaying in a web-based UI. It supports arbitrarily long traces so you can view how RSS changes over time. You can also issue SQL queries on the data it produces for offline processing. Enable long traces from the System Tracing app. Make sure the memory:Memory category is enabled for the trace.

heapprofd

heapprofd is a memory tracking tool that’s part of Perfetto. This tool can help you find memory leaks by showing where memory was allocated using malloc. heapprofd can be started using a Python script, and because the tool has low overhead, it doesn't affect performance like other tools such as Malloc Debug.

bugreport

bugreport is a logging tool for finding out whether or not your game crashed because it ran out of memory. The tool's output is much more detailed than using logcat. It’s useful for memory debugging because it shows if your game crashed because it ran out of memory or if it was killed by the LMK.

For more information, see Capture and read bug reports.