在游戏中有效管理内存

在 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 已回收了游戏仍然需要的页面,因此它会尝试从内存中重新加载这些页面。但由于空间不足,这些页面会不断地被换出(持续换入换出)。系统跟踪将这种情况报告为 kswapd 持续运行的线程。

内存抖动的一个症状是帧时间过长 - 可能达到一秒或更长时间。减少游戏的内存占用量可以解决这种情况。

使用可用工具

Android 提供了一系列工具来帮助了解系统如何管理内存。

Meminfo

此工具收集内存统计信息,以显示分配了多少 PSS 内存以及它用于哪些类别。

通过以下任一方式打印 meminfo 统计信息:

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

PrivateDirty 统计信息显示了进程内无法分页到磁盘且未与其他任何进程共享的 RAM 量。当该进程被终止时,此量的大部分将可供系统使用。

内存跟踪点

内存跟踪点会跟踪您的游戏正在使用的 RSS 内存量。计算 RSS 内存使用量比计算 PSS 使用量快得多。由于计算速度更快,RSS 在内存大小变化方面显示出更精细的粒度,从而可以更准确地测量峰值内存使用量。因此,更容易注意到可能导致游戏内存不足的峰值。

Perfetto 和长跟踪

Perfetto 是一套工具,用于收集设备上的性能和内存信息,并在基于 Web 的界面中显示。它支持任意长度的跟踪,因此您可以查看 RSS 随时间的变化。您还可以对它生成的数据执行 SQL 查询以进行离线处理。从系统跟踪应用启用长跟踪。确保为跟踪启用了 memory:Memory 类别。

heapprofd

heapprofd 是 Perfetto 的一部分,是一个内存跟踪工具。该工具可以通过显示使用 malloc 分配内存的位置来帮助您查找内存泄漏。heapprofd 可以使用 Python 脚本启动,并且由于该工具开销低,它不会像 Malloc Debug 等其他工具那样影响性能。

bugreport

bugreport 是一个日志记录工具,用于查明您的游戏是否因内存不足而崩溃。该工具的输出比使用 logcat 详细得多。它对于内存调试很有用,因为它显示了您的游戏是因内存不足而崩溃还是被 LMK 终止。

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