Speicherplatz in Spielen effizient verwalten

Auf der Android-Plattform versucht das System, so viel Systemspeicher (RAM) wie möglich zu nutzen, und führt verschiedene Speicheroptimierungen durch, um bei Bedarf Speicherplatz freizugeben. Diese Optimierungen können sich negativ auf Ihr Spiel auswirken, indem sie es verlangsamen oder ganz beenden. Weitere Informationen zu diesen Optimierungen finden Sie im Thema Speicherzuweisung zwischen Prozessen.

Auf dieser Seite wird beschrieben, wie Sie verhindern können, dass Ihr Spiel durch Situationen mit wenig Arbeitsspeicher beeinträchtigt wird.

Auf onTrimMemory() reagieren

Das System verwendet onTrimMemory(), um Ihre App über Lebenszyklusereignisse zu benachrichtigen, die eine gute Gelegenheit für Ihre App darstellen, die Arbeitsspeichernutzung freiwillig zu reduzieren und zu vermeiden, dass sie vom Low-Memory-Killer (LMK) beendet wird, um Arbeitsspeicher für andere Apps freizugeben.

Wenn Ihre App im Hintergrund beendet wird, kommt es beim nächsten Start durch den Nutzer zu einem langsamen Kaltstart. Apps, die ihren Arbeitsspeicherverbrauch im Hintergrund reduzieren, werden im Hintergrund seltener beendet.

Wenn Sie auf Ereignisse zum Kürzen von Speicher reagieren, sollten Sie große Speicherzuweisungen freigeben, die nicht sofort benötigt werden und bei Bedarf rekonstruiert werden können. Wenn Ihre App beispielsweise einen Cache mit Bitmaps hat, die aus lokal gespeicherten komprimierten Bildern decodiert wurden, ist es oft sinnvoll, diesen Cache als Reaktion auf TRIM_MEMORY_UI_HIDDEN zu verkleinern oder zu leeren.

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

Arbeitsspeicherbudgets vorsichtig einsetzen

Planen Sie den Arbeitsspeicher konservativ ein, um eine Überlastung zu vermeiden. Beachten Sie Folgendes:

  • Größe des physischen RAM: Spiele verwenden oft zwischen ¼ und ½ des physischen RAM auf dem Gerät.
  • Maximale zRAM-Größe: Mehr zRAM bedeutet, dass dem Spiel potenziell mehr Arbeitsspeicher zur Verfügung steht. Dieser Betrag kann je nach Gerät variieren. Suchen Sie in /proc/meminfo nach SwapTotal, um diesen Wert zu finden.
  • Arbeitsspeichernutzung des Betriebssystems: Geräte, die Systemprozessen mehr Arbeitsspeicher zuweisen, lassen weniger Arbeitsspeicher für Ihr Spiel übrig. Das System beendet den Prozess Ihres Spiels, bevor es Systemprozesse beendet.
  • Speichernutzung installierter Apps: Testen Sie Ihr Spiel auf Geräten, auf denen viele Apps installiert sind. Social-Media- und Chat-Apps müssen ständig ausgeführt werden und wirken sich auf den freien Speicherplatz aus.

Wenn Sie kein konservatives Arbeitsspeicherbudget einhalten können, sollten Sie einen flexibleren Ansatz wählen. Wenn das System Probleme mit zu wenig Arbeitsspeicher hat, reduzieren Sie die Menge an Arbeitsspeicher, die das Spiel verwendet. Weisen Sie beispielsweise Texturen mit niedrigerer Auflösung zu oder speichern Sie weniger Shader als Reaktion auf onTrimMemory(). Dieser dynamische Ansatz für die Speicherzuweisung erfordert mehr Arbeit vom Entwickler, insbesondere in der Spieldesignphase.

Seitenflattern vermeiden

Thrashing tritt auf, wenn der kostenlose Arbeitsspeicher niedrig ist, aber nicht niedrig genug, um das Spiel zu beenden. In diesem Fall hat kswapd Seiten zurückgefordert, die das Spiel noch benötigt. Daher wird versucht, die Seiten aus dem Arbeitsspeicher neu zu laden. Es ist nicht genügend Speicherplatz vorhanden, sodass die Seiten immer wieder ausgelagert werden (kontinuierliches Auslagern). System-Tracing meldet diese Situation als Thread, in dem kswapd kontinuierlich ausgeführt wird.

Ein Symptom für Thrashing sind lange Frame-Zeiten, die möglicherweise eine Sekunde oder länger dauern. Reduzieren Sie den Arbeitsspeicherbedarf des Spiels, um dieses Problem zu beheben.

Verfügbare Tools verwenden

Android bietet eine Reihe von Tools, mit denen Sie nachvollziehen können, wie das System den Arbeitsspeicher verwaltet.

Meminfo

Mit diesem Tool werden Arbeitsspeicherstatistiken erfasst, um zu zeigen, wie viel PSS-Arbeitsspeicher zugewiesen wurde und für welche Kategorien er verwendet wurde.

Sie haben folgende Möglichkeiten, die meminfo-Statistiken zu drucken:

  • Führen Sie den Befehl adb shell dumpsys meminfo package-name aus.
  • Verwenden Sie den MemoryInfo-Aufruf aus der Android Debug API.

Die Statistik PrivateDirty gibt die Menge an RAM im Prozess an, die nicht auf die Festplatte ausgelagert werden kann und nicht mit anderen Prozessen geteilt wird. Der Großteil dieses Betrags wird dem System zur Verfügung gestellt, wenn der Prozess beendet wird.

Speicher-Tracepoints

Mit Speicher-Tracepoints wird die Menge an RSS-Arbeitsspeicher erfasst, die von Ihrem Spiel verwendet wird. Die Berechnung der RSS-Speichernutzung ist viel schneller als die Berechnung der PSS-Nutzung. Da die Berechnung schneller ist, bietet RSS eine feinere Granularität bei Änderungen der Speichergröße und ermöglicht so genauere Messungen der maximalen Speichernutzung. Daher ist es einfacher, Spitzen zu erkennen, die dazu führen könnten, dass dem Spiel der Arbeitsspeicher ausgeht.

Perfetto und lange Traces

Perfetto ist eine Reihe von Tools zum Erfassen von Leistungs- und Speicherinformationen auf einem Gerät und zum Anzeigen in einer webbasierten Benutzeroberfläche. Es werden beliebig lange Traces unterstützt, sodass Sie sehen können, wie sich der RSS im Laufe der Zeit ändert. Sie können auch SQL-Abfragen für die Daten ausführen, die für die Offlineverarbeitung erstellt werden. Aktivieren Sie lange Traces in der System Tracing App. Achten Sie darauf, dass die Kategorie memory:Memory für den Trace aktiviert ist.

heapprofd

heapprofd ist ein Tool zur Speicherverfolgung, das Teil von Perfetto ist. Mit diesem Tool können Sie Speicherlecks finden, indem Sie sehen, wo Speicher mit malloc zugewiesen wurde. heapprofd kann mit einem Python-Script gestartet werden. Da das Tool nur geringen Overhead verursacht, wirkt es sich nicht auf die Leistung aus wie andere Tools wie Malloc Debug.

Fehlerbericht

bugreport ist ein Logging-Tool, mit dem Sie herausfinden können, ob Ihr Spiel abgestürzt ist, weil der Arbeitsspeicher nicht ausgereicht hat. Die Ausgabe des Tools ist viel detaillierter als bei der Verwendung von „logcat“. Das ist nützlich für das Debugging von Speicherproblemen, da es zeigt, ob Ihr Spiel abgestürzt ist, weil der Speicherplatz nicht ausgereicht hat, oder ob es durch den LMK beendet wurde.

Weitere Informationen finden Sie unter Fehlerberichte erfassen und lesen.