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 of lifecycle events that present a good opportunity for your
app to voluntarily reduce its memory usage and avoid being killed by the
low-memory killer (LMK)
to release memory for other apps to use.
If your app is killed in the background, then the next time the user launches your app they will experience a slow cold start. Apps that reduce their memory usage when going to the background are less likely to be killed in the background.
When responding to trim events, it's best to release large memory allocations
that are not immediately needed and could be reconstructed on demand. For
example, if your app has a cache of bitmaps that were decoded from locally
stored compressed images, then it's often a good idea to trim or purge this
cache in response to
TRIM_MEMORY_UI_HIDDEN
.
Kotlin
class MainActivity : AppCompatActivity(), ComponentCallbacks2 { override fun onTrimMemory(level: Int) { if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { // Release memory related to UI elements, such as bitmap caches. } if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) { // Release memory related to background processing, such as by // closing a database connection. } } }
Java
public class MainActivity extends AppCompatActivity implements ComponentCallbacks2 { public void onTrimMemory(int level) { switch (level) { if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { // Release memory related to UI elements, such as bitmap caches. } if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) { // Release memory related to background processing, such as by // closing a database connection. } } } }
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()) } }
Use the Memory Advice API beta
The Memory Advice API was developed as an
alternative to onTrimMemory that has much higher recall and precision at
predicting impending LMKs. The API achieves this by estimating the amount of
memory resources that are in use, and then notifying the app when certain
thresholds are exceeded. The API can also report the estimated percentage of
memory use directly to your app. You can use the Memory Advice API as an
alternative to
onTrimMemory
events for the purpose of memory management.
To use the Memory Advice API use the getting started guide.
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.
The PrivateDirty
statistic shows the
amount of RAM inside the process that can not be paged to disk and is not shared
with any other processes. The bulk of this amount becomes available to the
system when that process is killed.
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.