有效管理遊戲中的記憶體

在 Android 平台上,系統會嘗試盡可能使用系統記憶體 (RAM),並視情況執行各種記憶體最佳化作業,以釋出空間。這些最佳化作業可能會對您的遊戲造成負面影響,不是使遊戲速度變慢,就是使遊戲完全終止。如要進一步瞭解這些最佳化作業,請參閱「程序之間的記憶體配置」這個主題。

本頁面說明可以採取哪些步驟,來避免記憶體不足導致遊戲受到影響的情況。

回應 onTrimMemory()

系統會使用 onTrimMemory() 通知應用程式記憶體不足,而且該應用程式可能會因此終止。這往往會是應用程式收到的唯一警告。這個回呼相對於低內存殺手 (LMK) 延遲時間較長,因此快速回應回呼十分重要。

為了回應這項回呼,請降低分配速度、數量和大小。onTrimMemory() 傳遞常數代表事態嚴重,但您應該回應第一個警告,因為分配得比 onTrimMemory() 可回應的速度還快是有可能的。

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())
    }
}

使用 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,以找出這個值。
  • OS 的記憶體用量:為系統程序指定較多 RAM 的裝置,留給遊戲較少記憶體。在終止系統程序前,系統會先終止遊戲程序。
  • 安裝版應用程式的記憶體用量:在已安裝多個應用程式的裝置上測試遊戲。社群媒體和即時通訊應用程式必須持續執行,且會影響免費記憶體容量。

如果無法承諾以保守方式使用記憶體預算,請採取更為彈性的方法。如果系統碰到記憶體不足的問題,請減少遊戲使用的記憶體容量。例如,分配較低解析度的紋理或儲存較少著色器來回應 onTrimMemory()。尤其在遊戲設計階段,這種動態分配記憶體配置的方式會增加開發人員的工作量。

避免輾轉現象

當免費記憶體偏低,但又不至於導致遊戲終止時,就會發生「輾轉現象」。在這種情況下,kswapd 會收回遊戲所需的網頁,因此會嘗試從記憶體中重新載入網頁。空間不足,因此網頁會不斷替換 (連續替換)。 系統追蹤功能會將這個情形回報為 kswapd 持續執行的執行緒。

輾轉現象其中一個徵兆為影格時間較長,可能是一秒鐘或是更長。請減少遊戲的記憶體用量,以便解決這種情況。

使用可用的工具

Android 提供一系列工具,可協助您瞭解系統管理記憶體的方式。

Meminfo

這項工具會收集記憶體統計資料,以顯示 PSS 記憶體的分配情形,以及所使用的記憶體類別。

請以下列其中一種方式列印 meminfo 統計資料:

  • 使用 adb shell dumpsys meminfo package-name 指令:
  • 使用 Android Debug API 的 MemoryInfo 呼叫。

PrivateDirty 統計資料會顯示程序中無法分頁至磁碟,且不會與任何其他程序共用的 RAM 容量。當相應的程序終止時,大部分的用量會開放給系統使用。

記憶體追蹤點

記憶體追蹤點會追蹤遊戲使用的 RSS 記憶體量。計算記憶體用量的速度比計算 PSS 用量來得快。RSS 可更快速地進行計算,因此能更精細地分析記憶體大小的變化,以更準確地測量尖峰記憶體用量。 因此,您會更容易注意到造成遊戲記憶體不足的尖峰用量。

Perfetto 與長期追蹤記錄

Perfetto 是一套工具,用於收集裝置上的效能和記憶體資訊,並將其顯示在網頁式的使用者介面中。這項工具支援任意長度的追蹤記錄,因此您可以查看 RSS 隨著時間的變化情形。 也可以針對這項工具產生的資料執行 SQL 查詢,以進行離線處理。透過系統追蹤應用程式啟用長期追蹤記錄。確認已啟用追蹤記錄的 memory:Memory 類別。

heapprofd

heapprofd 是 Perfetto 中的一種記憶體追蹤工具。這項工具使用 malloc 顯示記憶體分配位置,可協助發現記憶體流失的情況。heapprofd 可以使用 Python 指令碼來啟動,而且由於此工具的負擔較低,因此不會像 Malloc Debug 等其他工具一樣影響效能。

bugreport

bugreport 是一款記錄工具,可協助您瞭解遊戲是否因記憶體不足而當機。相較於 Logcat,這項工具的輸出內容更為詳細。這對於記憶體偵錯很有幫助,因為這項工具會顯示遊戲是否因記憶體不足而當機,或是因 LMK 而終止。

詳情請參閱擷取和讀取錯誤報告一文。