Efektywne zarządzanie pamięcią w grach

Na platformie Androida system stara się wykorzystać jak najwięcej pamięci RAM i w razie potrzeby wykonuje różne optymalizacje pamięci, aby zwolnić miejsce. Te optymalizacje mogą mieć negatywny wpływ na grę – spowolnienie jej działania lub jej całkowite wyłączenie. Więcej informacji o tych optymalizacji znajdziesz w temacie Alokacja pamięci między procesy.

Na tej stronie opisujemy, jak uniknąć problemów z brakiem pamięci, które wpływa na grę.

Odpowiedź na onTrimMemory()

System używa parametru onTrimMemory(), aby powiadomić aplikację, że kończy się pamięć i aplikacja może zostać zatrzymana. W wielu przypadkach jest to jedyne ostrzeżenie, które aplikacja dostaje. Wywołanie zwrotne ma wysoki czas oczekiwania w porównaniu z meczem o małej ilości pamięci (LMK), dlatego tak ważne jest szybkie reagowanie na to wywołanie.

W odpowiedzi na to wywołanie zwrotne zmniejsz szybkość, liczbę i rozmiar przydziałów. onTrimMemory() przekazuje stałą wskazującą wagę, ale odpowiadaj na pierwsze ostrzeżenie, ponieważ możliwe jest przydzielanie szybciej, niż może zareagować 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())
    }
}

Korzystanie z interfejsu Memory Advice API w wersji beta

Interfejs Memory Advice API został stworzony jako alternatywa dla onTrimMemory, który zapewnia znacznie większą czułość i precyzję w przewidywaniu nadchodzących zespołów LMK. Aby to osiągnąć, interfejs API szacuje ilość używanych zasobów pamięci, a potem powiadamia aplikację o przekroczeniu określonych wartości progowych. Interfejs API może też raportować szacowany odsetek wykorzystania pamięci bezpośrednio w aplikacji. Interfejs Memory Advice API może być alternatywą dla zdarzeń onTrimMemory do zarządzania pamięcią.

Aby korzystać z interfejsu Memory Advice API, zapoznaj się z przewodnikiem dla początkujących.

Stosuj rozsądne budżety na pamięć

Ograniczaj ilość pamięci, aby uniknąć wyczerpania pamięci. Oto kilka kwestii, które warto wziąć pod uwagę:

  • Rozmiar fizycznej pamięci RAM: gry wykorzystują często od 1⁄4 do 1⁄2 ilości fizycznej pamięci RAM na urządzeniu.
  • Maksymalny rozmiar pamięci RAMRAM: więcej pamięci RAMRAM oznacza, że gra może mieć więcej pamięci do przydzielenia. Ta wartość może się różnić w zależności od urządzenia. Aby ją znaleźć, poszukaj fragmentu SwapTotal w polu /proc/meminfo.
  • Wykorzystanie pamięci systemu operacyjnego: urządzenia, które wyznaczają więcej pamięci RAM procesom systemowym, pozostawiają mniej pamięci dla gry. System zamyka procesy gry, zanim zniszczy procesy systemowe.
  • Wykorzystanie pamięci przez zainstalowane aplikacje: możesz przetestować grę na urządzeniach z zainstalowanymi aplikacjami. Aplikacje do mediów społecznościowych i czatu muszą stale działać i określać ilość wolnej pamięci.

Jeśli nie możesz wybrać takiego budżetu, przyjmij bardziej elastyczne podejście. Jeśli w systemie występują problemy z małą ilością pamięci, zmniejsz ilość pamięci używanej przez grę. Na przykład przydziel tekstury o niższej rozdzielczości lub przechowuj mniej cieniowania w odpowiedzi na żądanie onTrimMemory(). Takie dynamiczne podejście do alokacji pamięci wymaga od dewelopera więcej pracy, zwłaszcza na etapie projektowania gry.

Unikaj szarpania

Trzęsienie ma miejsce, gdy ilość wolnej pamięci jest niska, ale jest ona niewystarczająca, aby zakończyć działanie gry. W tej sytuacji kswapd odzyskał(a) strony, których gra nadal potrzebuje, więc próbuje ponownie załadować je z pamięci. Za mało miejsca, więc strony są zastępowane (ciągła zamiana). Śledzenie systemu zgłasza tę sytuację jako wątek, w którym kswapd działa w sposób ciągły.

Jednym z objawów ubijania jest długi czas renderowania klatki, który czasem trwa co najmniej sekundę. Aby rozwiązać ten problem, zmniejsz rozmiar pamięci gry.

Korzystanie z dostępnych narzędzi

Android ma zbiór narzędzi, które pomagają zrozumieć, jak system zarządza pamięcią.

Informacje o pamięci

To narzędzie gromadzi statystyki dotyczące pamięci, aby pokazać, ile pamięci PSS zostało przydzielone oraz kategorie, w przypadku których była używana.

Statystyki meminfo możesz wydrukować na jeden z tych sposobów:

  • Użyj polecenia adb shell dumpsys meminfo package-name.
  • użyj wywołania MemoryInfo z interfejsu Android Debug API.

Statystyka PrivateDirty pokazuje ilość pamięci RAM w procesie, której nie można stronicować na dysk i której nie można udostępnić innym procesom. Większość tej ilości staje się dostępna dla systemu po zakończeniu tego procesu.

Punkty śledzenia pamięci

Punkty śledzenia pamięci śledzą ilość pamięci RSS używanej przez grę. Obliczanie wykorzystania pamięci RSS jest znacznie szybsze niż obliczanie wykorzystania PSS. Ponieważ obliczenia są szybsze, RSS pokazuje bardziej szczegółowe informacje o zmianach rozmiaru pamięci, co pozwala dokładniej mierzyć szczytowe wykorzystanie pamięci. Dzięki temu łatwiej jest dostrzec szczyty, które mogą powodować za mało pamięci w grze.

Perfetto i długie ślady

Perfetto to zestaw narzędzi do gromadzenia informacji o wydajności i pamięci urządzenia oraz do wyświetlania ich w internetowym interfejsie użytkownika. Obsługuje dowolnie długie ślady, dzięki czemu możesz obserwować, jak kanał RSS zmienia się w czasie. Możesz też wysyłać zapytania SQL do danych na potrzeby przetwarzania offline. Włącz długie logi czasu w aplikacji Śledzenie systemu. Sprawdź, czy dla logu czasu włączona jest kategoria memory:Memory.

Heapprofd

heapprofd to narzędzie do śledzenia pamięci, które jest częścią Perfetto. To narzędzie może pomóc Ci znaleźć wycieki pamięci, pokazując, gdzie pamięć została przydzielona za pomocą malloc. heapprofd można zacząć przy użyciu skryptu Pythona, a ponieważ narzędzie jest mało wydajne, nie wpływa na wydajność tak jak inne narzędzia, takie jak Malloc Debug.

zgłoszenie błędów

bugreport to narzędzie do logowania, które pozwala sprawdzić, czy gra uległa awarii z powodu wyczerpania pamięci. Dane wyjściowe narzędzia są znacznie bardziej szczegółowe niż dane uzyskane za pomocą narzędzia logcat. Jest przydatny przy debugowaniu pamięci, bo pokazuje, czy gra uległa awarii z powodu wyczerpania pamięci lub jeśli została przerwana przez LMK.

Więcej informacji znajdziesz w artykule Rejestrowanie i odczytywanie raportów o błędach.