在游戏中有效地管理内存

在 Android 平台上,系统会尝试尽可能地使用系统内存 (RAM),并在需要时执行各种内存优化来释放空间。这些优化可能会给您的游戏带来负面影响,因为它们可能会减慢游戏运行速度或使游戏终止。如需详细了解这些优化,请参阅主题进程间的内存分配

本页介绍了您可以采取哪些措施来避免内存不足对您的游戏造成影响。

响应 onTrimMemory()

系统使用 onTrimMemory() 将生命周期事件通知给您的应用,这些事件对您来说 主动减少其内存使用量,并避免被 低内存终止守护程序 (LMK) 释放内存供其他应用使用。

如果您的应用在后台终止,则用户下次启动时 那么用户在使用您的应用时 冷启动。可减少 不大会在进入后台时终止内存占用 背景。

响应修剪事件时,最好释放较大的内存分配 没有立即需要且可根据需要重建的用例。例如,如果您的应用包含从本地存储的压缩图片解码的位图缓存,则通常最好在响应 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())
    }
}

使用 Memory Advice API Beta 版

Memory Advice API 是作为一种 onTrimMemory 的替代方案,具有更高的召回率和精确率, 预测即将到来的 LMK。为实现这一目标,API 会估算 并在收到特定消息时通知应用 阈值。该 API 还可以报告 内存使用直接提供给您的应用。您可以将 Memory Advice API 用作 替代 onTrimMemory 以便管理内存

如需使用 Memory Advice API,请参阅入门指南

保守地分配内存预算

保守地分配内存预算,以避免耗尽内存。下面列出了一些需要注意的事项:

  • 物理 RAM 的大小:游戏通常会占用设备上物理 RAM 量的 ¼ 到 ½。
  • zRAM 大小上限:zRAM 越大,可供游戏分配的内存可能就越多。此上限可能因设备而异;如需获取此值,请查看 /proc/meminfo 中的 SwapTotal
  • 操作系统的内存用量:设备为系统进程分配的 RAM 越多,留给游戏的内存就越少。内存不足时,系统会优先终止游戏进程,之后如果内存仍然不足,系统才会终止系统进程。
  • 已安装的应用的内存用量:在安装了许多应用的设备上测试您的游戏。社交媒体和聊天应用需要持续运行,并会影响可用内存量。

如果您无法保守地分配内存预算,可以采取一种更灵活的方法。系统内存不足时,请减少游戏使用的内存量。例如,在响应 onTrimMemory() 时为游戏分配分辨率较低的纹理或存储较少的着色器。这种动态的内存分配方式需要开发者做更多工作,尤其是在游戏设计阶段。

避免抖动

可用内存不足,但不至于导致系统终止游戏时,就会出现抖动。在这种情况下,由于 kswapd 已回收游戏仍需要的页面,因此游戏会尝试从内存中重新加载这些页面。但由于空间不足,这些页面会被不断换出(持续发生交换)。System Tracing 会将这种情况报告为一个线程,kswapd 在该线程中持续运行。

抖动的一个特征是帧时间较长,可能为一秒或更长时间。减少游戏的内存占用量可以解决这种情况。

使用可用工具

Android 中包含一系列旨在帮助您了解系统如何管理内存的工具。

Meminfo

此工具用于收集内存统计信息,以便显示已分配的 PSS 内存量,以及使用这些内存的进程所属的类别。

可以通过以下任一方式输出 meminfo 统计信息:

  • 使用命令 adb shell dumpsys meminfo package-name
  • 使用 Android Debug API 中的 MemoryInfo 调用。

PrivateDirty 统计信息中会显示进程内无法分页到磁盘并且不能与任何其他进程共用的 RAM 量。相应进程被终止后,大部分此类内存将可供系统使用。

内存跟踪点

内存跟踪点会跟踪游戏正在使用的 RSS 内存量。计算 RSS 内存用量要比计算 PSS 内存用量快得多。由于计算速度较快,因此 RSS 数据会更精细地显示内存大小的变化,从而有助于更准确地计算峰值内存用量。因此,您会更容易注意到可能导致游戏耗尽内存的峰值。

Perfetto 和长跟踪记录

Perfetto 是一套工具,用于收集设备上的性能和内存信息,并将其显示在基于网页的界面中。它支持任意长度的跟踪记录,因此您可以查看 RSS 的持续变化情况。您还可以对该工具生成的数据执行 SQL 查询,以进行离线处理。通过 System Tracing 应用启用长跟踪记录。确保为跟踪记录启用 memory:Memory 类别。

heapprofd

heapprofd 是 Perfetto 中的一种内存跟踪工具。此工具可使用 malloc 显示内存分配位置,从而有助于您发现内存泄漏。可以使用 Python 脚本来启动 heapprofd,而且由于该工具的开销较低,因此它不会像 Malloc 调试等其他工具一样影响性能。

bugreport

bugreport 是一个日志记录工具,用于确定游戏是否因为内存耗尽而崩溃。此工具的输出远比使用 logcat 得到的结果更详尽。它对于内存调试来说很有用,因为它可以显示游戏是因为内存耗尽而崩溃的,还是被 LMK 终止的。

如需了解更多信息,请参阅获取和阅读错误报告