# Android OS on Snapdragon

## Introduction

This guide is for Android game developers of native-C++ performance-intensive games that are intended to dominate the phone (unless the player is interrupted by a phone call, text, etc), and not interact with any other Android services, processes or Activity’s.

These are Qualcomm’s best recommendations for management of memory and your app’s Android lifecycle.

## Memory Management on Android

Snapdragon hardware uses unified memory, which means CPU and GPU allocations coexist in the same memory space.  This means it’s possible for GPU allocations to cause the CPU to run out of memory and vice versa.

Keeping this in mind, let’s examine some of the tools available to profile CPU and GPU memory usage on Android.

Common engines and platforms provide their own memory profiling tools:

- **Unreal5** : [https://docs.unrealengine.com/5.0/en-US/memory-insights-in-unreal-engine/](https://docs.unrealengine.com/5.0/en-US/memory-insights-in-unreal-engine/)
- **Unreal4** : [https://docs.unrealengine.com/4.27/en-US/TestingAndOptimization/PerformanceAndProfiling/Profiler/](https://docs.unrealengine.com/4.27/en-US/TestingAndOptimization/PerformanceAndProfiling/Profiler/)
- - **Unity** :
    - - [https://docs.unity3d.com/Manual/ProfilerMemory.html](https://docs.unity3d.com/Manual/ProfilerMemory.html)
    - [https://blog.unity.com/engine-platform/analyzing-physical-memory-footprint-using-memory-profiler](https://blog.unity.com/engine-platform/analyzing-physical-memory-footprint-using-memory-profiler)
- - **Oculus Quest** :
    - - [https://developer.oculus.com/blog/getting-a-handle-on-meta-quest-memory-usage/](https://developer.oculus.com/blog/getting-a-handle-on-meta-quest-memory-usage/)
    - [https://developer.oculus.com/documentation/unity/ts-gpumeminfo/](https://developer.oculus.com/documentation/unity/ts-gpumeminfo/)
    - [https://developer.oculus.com/documentation/native/android/ts-ovrmetricstool/](https://developer.oculus.com/documentation/native/android/ts-ovrmetricstool/)

You may have some of the following questions (especially if you’re not using one of the above engines):

### In Kotlin/Java, what is the best approximation of “how much memory can my app allocate?”

ActivityManager::MemoryInfo.availMem - ActivityManager::MemoryInfo.threshold
    Copy to clipboard

…in bytes

[https://developer.android.com/reference/android/app/ActivityManager.MemoryInfo](https://developer.android.com/reference/android/app/ActivityManager.MemoryInfo)

To be clear: “availMem” is not the amount of memory Android believes is available for your app – it’s “availMem - threshold”.

Note that even if the user is only playing your game, it’s possible for background processes to allocate more memory at any time, reducing the amount of memory your app can consume.  See the [onTrimMemory() callbacks](https://developer.android.com/reference/android/content/ComponentCallbacks2) to deal with this scenario.

### What is the easiest way to see what memory your app has allocated over time?

Android Studio’s Profiler tracks a running app’s memory footprint over time, classifying allocated memory into several broad categories (for example, Java, C++, and several subcategories of graphics): [https://developer.android.com/studio/profile](https://developer.android.com/studio/profile)

For an in-depth CPU memory allocation analysis, the Perfetto tool has a learning curve, but is powerful:
[https://perfetto.dev/docs/quickstart/android-tracing](https://perfetto.dev/docs/quickstart/android-tracing)

If neither the Android Studio nor Perfetto tools suit you, read on for Kotlin/Java and adb methods of profiling memory.

### In Kotlin/Java what is the best approximation of memory your app has natively allocated?

Debug::MemoryInfo.getTotalPrivateDirty()
    Copy to clipboard

…in kilobytes.  See: [https://developer.android.com/reference/android/os/Debug.MemoryInfo](https://developer.android.com/reference/android/os/Debug.MemoryInfo)

### In Kotlin/Java, what is the best approximation of graphics memory used by your app?

Debug::MemoryInfo.debugMemoryInfo.getMemoryStat("summary.graphics")
    Copy to clipboard

…in kilobytes.  See: [https://developer.android.com/reference/android/os/Debug.MemoryInfo#getMemoryStat(java.lang.String](https://developer.android.com/reference/android/os/Debug.MemoryInfo#getMemoryStat%28java.lang.String))

### In adb, what is the best approximation of total memory used by your app?

adb shell dumpsys meminfo <your_app_name>
    Copy to clipboard

…outputs something like:

Pss  Private  Private  SwapPss     Heap     Heap     Heap
                    Total    Dirty    Clean    Dirty     Size    Alloc     Free
                   ------   ------   ------   ------   ------   ------   ------
     Native Heap  2136406  2136216        0        0  4214784  2114445  2100338
     Dalvik Heap     1245     1228        0        0     2431      895     1536
    Dalvik Other      972      972        0        0
           Stack       64       64        0        0
          Ashmem        2        0        0        0
         Gfx dev     1304      912      392        0
       Other dev       12        0       12        0
        .so mmap     4217      376      620        0
       .apk mmap      398        0       36        0
       .ttf mmap       47        0        0        0
       .dex mmap     5082        4     4084        0
       .oat mmap      109        0        4        0
       .art mmap     4172     3788      172        0
      Other mmap       22        4        0        0
      EGL mtrack    44400    44400        0        0
       GL mtrack    19168    19168        0        0
         Unknown     1095     1092        0        0
           TOTAL  2218715  2208224     5320        0  4217215  2115340  2101874
    Copy to clipboard

The relevant column, in kilobytes, is “Private Dirty”:

TOTAL:              2208224
    Copy to clipboard

Note that this doesn’t include any memory that’s shared between your app’s process and other processes (for example, \*.so libraries shared between multiple processes), and that while “Pss Total” adds a fraction of shared memory usage to “Private Dirty”, it doesn’t account for all of it.
[https://developer.android.com/topic/performance/memory-management](https://developer.android.com/topic/performance/memory-management)

### In adb, what is the best approximation of memory available across the entire device?

adb shell cat proc/meminfo
    Copy to clipboard

…outputs a number of fields; the relevant one is:

MemAvailable:       2093732 kB
    Copy to clipboard

Note that MemAvailable is the best approximation from adb, but is usually less accurate than ActivityManager::MemoryInfo in the app’s Kotlin/Java code, as shown above.

### In adb, what is the easiest way of approximating an app’s memory footprint over time?

adb shell dumpsys procstats --hours 1
    Copy to clipboard

…outputs something like:

* <your_app_name> / u0a280 / v1:
       TOTAL: 6.3% (1.1GB-1.6GB-2.1GB/1.1GB-1.6GB-2.1GB over 2)
         Top: 6.3% (1.1GB-1.6GB-2.1GB/1.1GB-1.6GB-2.1GB over 2)
    (Cached): 0.65%
    Copy to clipboard

### In adb, what’s the best way to get the total graphics memory footprint across the entire device?

(Requires root access)

adb shell "cat /sys/class/kgsl/kgsl/page_alloc"
    Copy to clipboard

…outputs, in bytes:

611381248
    Copy to clipboard

Note that two invocations of this command “before running your app” and “after running your app” typically isn’t equivalent to the “Graphics” field below, since Android’s memory management includes purposeful overallocation, sharing common memory objects, and other features.

### In adb, what’s the best way of examining an app’s graphics memory footprint?

adb shell dumpsys meminfo <your_app_name>
    Copy to clipboard

…outputs, in kilobytes:

Gfx dev:256448
    EGL mtrack:44400
    GL mtrack:263124
    Copy to clipboard

…and the sum of these three values is output like:

Graphics: 563972
    Copy to clipboard

### In adb, what’s the best way of getting a finer-grained picture of an app’s graphics memory usage?

On some devices, with root access, it’s possible to more precisely classify graphics memory allocations.  However, this doesn’t work on all Android installations due to kernels’ varying security settings.

Attempt more precise classification with:

adb shell cat /sys/kernel/debug/kgsl/proc/<proc-id>/mem
    Copy to clipboard

For example, if process 4190 is an OpenGL app, then:

adb shell cat /sys/kernel/debug/kgsl/proc/4190/mem > C:\kgsl_mem.txt
    Copy to clipboard

…might produce allocations classified as one of:

- command
- texture
- gl
- any
- arraybuffer
- renderbuffer
- egl\_surface
- renderbuffer

If process 4190 is a Vulkan app, it might produce allocations classified as one of:

- vk\_any
- vk\_cmdbuffer
- vk\_descriptorpool
- vk\_device
- vk\_devicememory
- vk\_image
- vk\_querypool
- vk\_queue

### In adb, what is the best way of approximately reporting an app’s memory leaks?

adb shell dumpsys meminfo --unreachable <your_app_name> | (find "unreachable allocations")
    Copy to clipboard

…outputs something like:

1886402728 bytes in 3605 unreachable allocations
    Copy to clipboard

(This app purposefully leaked a lot of memory – more typically only kilobytes of “leaked” memory is reported.  This command typically reports a small amount of “leaked” memory, even for apps that manage memory perfectly).

### What is the best way of investigating an app’s memory corruption?

Android Game Development Environment for Visual Studio investigates a wide array of memory errors: [https://developer.android.com/games/agde/address-sanitizer](https://developer.android.com/games/agde/address-sanitizer)

### How can I instrument C++ malloc calls?

[https://android.googlesource.com/platform/bionic/+/main/libc/malloc_hooks/README.md](https://android.googlesource.com/platform/bionic/+/main/libc/malloc_hooks/README.md)

## Android Application Lifecycle Guidelines

### NativeActivity Dependency Minimization

Ideally, your app keeps all its resources within its process – for example, within its NativeActivity.  This guarantees that Android will reclaim all resources (memory and otherwise) upon closing the app.

Avoid launching other services as much as possible.

### Lifecycle Callbacks

onDestroy() is not guaranteed to be called; don’t use it.

onPause() means your app is backgrounded (though sometimes still fully visible, as in multi-window mode), and hence more vulnerable to being killed.  onPause() is often given a very brief execution window, so don’t use it – or, if you must, perform only fast operations (like logging the event).

onResume() means the user has restored the app after an onPause().  The app is now less vulnerable to being killed.

onStop() means the app is no longer visible, and is more likely to be killed than after onPause().  onStop() is where any resources outside of the Activity should be released to avoid leaking them (Android will reclaim all resources within the process, so such resources require no work from you).

If the user restores the app, the app receives onRestart().

onStop() won’t be called if the system is [under extreme memory pressure, in which case the system can kill the application process at any time](https://developer.android.com/reference/android/app/Activity).  It is best to keep all resources in the NativeActivity (which keeps them in the app’s process).

onStop() is a good place to perform any “save-game” processing so the player doesn’t lose progress if the app is killed – even though this event may not occur when “under extreme memory pressure”:

For more, see:

- [https://developer.android.com/reference/android/app/Activity](https://developer.android.com/reference/android/app/Activity)
- [https://developer.android.com/guide/components/activities/activity-lifecycle](https://developer.android.com/guide/components/activities/activity-lifecycle)

### Lowmemorykiller Management

Android almost always gives an app one or more warnings before terminating it to redistribute the app’s memory to processes Android considers more important.

Handling onTrimMemory() callbacks is the most reliable way of discouraging Android from killing your app, since as of this writing the newer MemoryAdvice method isn’t supported across all devices.

TRIM\_MEMORY\_RUNNING\_MODERATE, TRIM\_MEMORY\_RUNNING\_LOW and TRIM\_MEMORY\_RUNNING\_CRITICAL are warnings your app would ideally respond to by:

- reducing memory usage as much as possible
- performing any “save-game” processing if this has not been done recently, so that if the app is killed after all, the user loses little to no progress

TRIM\_MEMORY\_RUNNING\_CRITICAL means your app might be killed – in some cases, without even calling onStop().

Upon receiving onStop() or any TRIM\_MEMORY\_RUNNING\_\* onTrimMemory() callback, we recommend performing any “save-game” processing such that the player doesn’t lose progress if the app is killed.

Since TRIM\_MEMORY\_RUNNING\_MODERATE, TRIM\_MEMORY\_RUNNING\_LOW or TRIM\_MEMORY\_RUNNING\_CRITICAL onTrimMemory() callbacks can happen repeatedly and quickly, consider performing “save-game” processing immediately upon first receiving such a callback, and then again every minute so long as these callbacks continue.

TRIM\_MEMORY\_UI\_HIDDEN means that your app’s UI is no longer visible, so this is a good place to release large resources that are used only by your UI.  This is a lifecycle event notification and an opportunity to release UI memory – not a low-memory warning.

When your app is backgrounded, you might get some onTrimMemory() warnings (onTrimMemory() tends to return TRIM\_MEMORY\_COMPLETE repeatedly while the app is in the background, but unlike the TRIM\_MEMORY\_RUNNING warnings this does not necessarily indicate a low-memory scenario).

We recommend that after you perform any “save-game” processing, decide if you want to try reducing your app’s footprint in response to these warnings, or just risk the app being killed and forcing the player to reload from the last saved state.

Prior to Oct 18, 2011 (Android 4.0/Ice Cream Sandwich), the onTrimMemory() callbacks don’t exist, and ActivityManager::MemoryInfo.lowMemory flag is all you have to indicate a low-memory scenario that is more likely to kill your app – possibly without even calling onStop().  Hopefully you are comfortable ignoring devices with Android installations this old.

For more, see:

[https://developer.android.com/topic/performance/memory](https://developer.android.com/topic/performance/memory)

### Multi-Window Disabling

We recommend you explicitly disable multi-window (for simplicity) with the following:

<application
        android:name=".MyActivity"
        android:resizeableActivity="false"
        android:supportsPictureInPicture="false"
    Copy to clipboard

If resizeableActivity is set to true, the activity can be launched in split-screen and free-form modes.

If the attribute is set to false (as we recommend), the activity does not support multi-window mode – and if the user attempts to launch the activity in multi-window mode anyway, the activity takes over the full screen instead.

For more, see:

[https://developer.android.com/guide/topics/manifest/activity-element](https://developer.android.com/guide/topics/manifest/activity-element)

### Identifiers That Can’t Change After Publishing

Know which identifiers your app cannot change after publishing: [https://android-developers.googleblog.com/2011/06/things-that-cannot-change.html](https://android-developers.googleblog.com/2011/06/things-that-cannot-change.html)

Last Published: Mar 03, 2026

[Previous Topic
Android](https://docs.qualcomm.com/bundle/publicresource/80-78185-2/topics/android.md) [Next Topic
Understanding and resolving Graphics Memory Loads](https://docs.qualcomm.com/bundle/publicresource/80-78185-2/topics/gmem_loads.md)