Informacje o zarządzaniu pamięcią

Optymalizacja pamięci ma kluczowe znaczenie dla zapewnienia stabilnej i wydajnej rozgrywki na Androidzie. Z tego przewodnika dowiesz się, dlaczego wydajność pamięci jest ważna, jak system operacyjny Android zarządza limitami pamięci procesów oraz jakie nowe dane dotyczące pamięci są dostępne w Konsoli Google Play, aby pomóc Ci monitorować i ulepszać jakość techniczną gry.

Znaczenie optymalizacji pamięci

Optymalizacja pamięci gry jest niezbędna do utrzymania graczy, rozszerzenia zgodności z urządzeniami i spełnienia standardów jakości platformy:

  • Zapobieganie zimnemu startowi (wrażenia użytkownika i utrzymanie): gdy gracz tymczasowo przełącza się z gry (np. aby odpowiedzieć na powiadomienie lub sprawdzić wiadomość), system operacyjny umieszcza proces gry w tle. Jeśli wykorzystanie pamięci w tle przez grę jest zbyt wysokie, systemowy mechanizm LMK (Low Memory Killer) priorytetowo traktuje zakończenie procesu gry, aby odzyskać pamięć RAM na potrzeby zadań na pierwszym planie. Gdy użytkownik wznowi grę, zamiast płynnego i natychmiastowego wznowienia na ciepło, gra musi przejść długi zimny start – całkowicie przeładować z pamięci duże zasoby graficzne, dźwięk i pliki binarne silnika gry. Utrzymywanie niskiego wykorzystania pamięci w tle zapobiega takim cichym zamknięciom, zachowując stan użytkownika i umożliwiając graczom natychmiastowe wznowienie sesji. Więcej informacji o działaniu systemowego LMK znajdziesz w przewodniku Android Vitals – Low memory killers.
  • Stabilność ekosystemu i urządzenia: nieefektywne wykorzystanie pamięci i wycieki pamięci pogarszają ogólny stan systemu. Gdy pamięć systemowa jest na wyczerpaniu, system jest mocno obciążony, co powoduje spadek liczby klatek, zacinanie się interfejsu i problemy z dźwiękiem. Jeśli obciążenie pamięci jest zbyt duże, systemowy mechanizm Low Memory Killer (LMK) agresywnie kończy procesy w tle, co powoduje, że inne aplikacje wolno się uruchamiają i tracą stan użytkownika, gdy gracze przełączają się między zadaniami.
  • Zamykanie na poziomie platformy: od Androida 17 (poziom interfejsu API 37) system bardziej aktywnie zamyka procesy, które zużywają zbyt dużo pamięci. Jeśli gra zajmuje zbyt dużo miejsca, system operacyjny może nagle zakończyć jej proces bez wygenerowania standardowego zrzutu stosu.
  • Zgodność z urządzeniami: flagowe urządzenia mają od 12 GB do 16 GB pamięci RAM, ale ogromna część graczy na całym świecie korzysta z urządzeń z 4 GB lub 6 GB pamięci RAM. Prawidłowe zarządzanie pamięcią zapewnia, że gra pozostaje dostępna i responsywna na wszystkich poziomach sprzętowych bez konieczności stosowania złożonych, oddzielnych pakietów zasobów.

Informacje o pamięci w Androidzie

Aby opracować skuteczne strategie zarządzania pamięcią, deweloperzy muszą zrozumieć, jak platforma Androida zarządza pamięcią fizyczną i jak mierzy aktywny ślad gry.

Podstawowe pojęcia związane z pamięcią Androida

Podstawowe informacje o zarządzaniu pamięcią na poziomie platformy znajdziesz w oficjalnej dokumentacji Zarządzanie pamięcią – omówienie. Ten zasób obejmuje 4 obszary architektury:

  • Omówienie pamięci: Android używa stronicowania i mapowania pamięci (mmap) do zarządzania pamięcią RAM. Nie obsługuje tradycyjnego pliku wymiany na dysku, ale korzysta z kompresji stron (za pomocą zRAM) i odzyskiwania stron, aby zwolnić pamięć fizyczną.
  • Alokacja pamięci między procesami: Android udostępnia pamięć RAM w całym systemie. Przypisuje określone sterty do wykonywania maszyn wirtualnych Dalvik lub ART, umożliwiając jednocześnie natywnym środowiskom programistycznym (takim jak silniki gier w C++) żądanie pamięci z natywnej sterty systemowej.
  • Zarządzanie pamięcią aplikacji: Android działa w modelu wieloprocesowym i oczekuje, że aplikacje będą dynamicznie monitorować stan swojego cyklu życia i dobrowolnie zwalniać niepotrzebne zasoby (takie jak niebuforowane grafiki i mapy bitowe), aby wspierać działanie systemu.
  • Omówienie procesów i wątków: system kategoryzuje procesy w hierarchii na podstawie ich bieżącej widoczności i ważności z perspektywy użytkownika, określając, które procesy są utrzymywane przy życiu, a które są najpierw przerywane w przypadku braku pamięci.

Wskaźnik całkowitego wykorzystania pamięci

Ogranicznik pamięci na poziomie platformy w Androidzie 17 ocenia wykorzystanie procesów na podstawie całkowitego wykorzystania pamięci, a nie całkowitego rozmiaru pamięci rezydentnej (RSS) czy rozmiaru pamięci wirtualnej.

Łączne wykorzystanie pamięci = anonimowa pamięć RSS (RssAnon) + nieskompresowana przestrzeń wymiany (VmSwap)

Aby zapobiec przekraczaniu limitów platformy przez gry, musisz dokładnie zrozumieć, co te dane oznaczają na poziomie systemu. Więcej informacji o tych danych, przydziałach fizycznej pamięci RAM i sposobie obsługi stron opartych na plikach znajdziesz w sekcji Informacje o danych RSS i danych dotyczących pamięci wymiany w przewodniku Monitorowanie wykorzystania pamięci.

Ograniczenia dotyczące pamięci

Aby utrzymać stabilność systemu i zapobiec nadmiernemu zużyciu zasobów przez aplikacje, platforma Androida zarządza limitami pamięci dla uruchomionych procesów.

Ogranicznik pamięci w Androidzie 17 i nowszych wersjach

Android 17 (poziom interfejsu API 37) i nowsze wersje zarządzają rygorystycznymi limitami pamięci na aplikację za pomocą cgroup v2 w systemie Linux, aby zapobiec niestabilności systemu spowodowanej przez poszczególne aplikacje. Więcej informacji o implementacji technicznej znajdziesz w przewodniku po ograniczniku pamięci AOSP i na blogu o priorytetowym traktowaniu wydajności pamięci i najważniejszych krokach w Androidzie 17.

  • Mechanizm: ogranicznik pamięci monitoruje wszystkie procesy aplikacji i dynamicznie przypisuje limity na podstawie stanu cyklu życia procesu:
    • Widoczne procesy (na pierwszym planie): procesy aplikacji, które obecnie wyświetlają interfejs, powinny działać w większym zestawie zasobów i mają większy limit.
    • Niewidoczne procesy (w tle lub usługi): procesy aplikacji, które wykonują aktywne zadania bez wyświetlania interfejsu, są ograniczone do bardziej rygorystycznego budżetu.
  • Atrybuty jądra: usługa opiera się na 2 głównych atrybutach:
    • memory.high: limit elastyczny. Po przekroczeniu tego limitu jądro ogranicza proces i próbuje agresywnie odzyskać pamięć. Może to spowodować pogorszenie wydajności gry.
    • memory.swap.max: zarządza twardym limitem przestrzeni wymiany lub zRAM, którą może wykorzystać proces.
  • Zachowanie po zakończeniu: jeśli proces nadal przydziela pamięć anonimową powyżej memory.high i wyczerpuje pojemność pamięci wymiany, przydzielanie kończy się niepowodzeniem, a system operacyjny po cichu kończy proces. To zakończenie jest rejestrowane za pomocą ApplicationExitInfo z powodem zakończenia Ogranicznik pamięci (dostępne od Androida 17, 26Q4).

Nowe limity pamięci w Android Vitals w Konsoli Play

Aby pomóc deweloperom w proaktywnym identyfikowaniu problemów z pamięcią, Google Play wprowadza nowe dane w Android Vitals w Konsoli Play. Konsola Play śledzi 90 percentyl (P90) anonimowego wykorzystania pamięci RSS + Swap podczas sesji gry, aby identyfikować skrajne wartości odstające.

Progi ostrzeżeń i egzekwowania są skalowane na podstawie fizycznej pojemności pamięci RAM urządzenia i stanów procesów. Te limity są stosowane w 2 różnych fazach.

Szczegółowe wytyczne i limity pamięci znajdziesz w sekcji „Jakie są progi niewłaściwego działania?” w artykule Android Vitals.

Usługi widoczne dla użytkowników

Usługi widoczne to krytyczne procesy w tle, które system Android uważa za zauważalne dla użytkownika. Ten stan obejmuje wszystkie uruchomione procesy:

  • Usługi działające na pierwszym planie
  • Zadania priorytetowe
  • Zadania przenoszenia danych inicjowane przez użytkownika
  • usługi powiązane z systemem lub usługi powiązane przez inne aplikacje;

Usługi postrzegalne są przeznaczone do wykonywania krytycznych, długotrwałych zadań w tle, dlatego są bardzo podatne na skumulowane wycieki cyklu życia. Limity pamięci platformy Androida traktują ten stan jako niebędący na pierwszym planie, co oznacza, że jeśli gra działa w tle, ale nadal korzysta z usługi postrzegalnej, podlega bardziej rygorystycznym limitom pamięci w tle lub pamięci usługi, które są podane na stronie Centrum pomocy Google Play. Gry muszą agresywnie przycinać niepotrzebne zasoby podczas przechodzenia z pierwszego planu do stanu usługi postrzegalnej w tle.

Wymagania dotyczące R8

Aby zminimalizować rozmiar kodu bajtowego i zmniejszyć podstawowe obciążenie procesów Javy, Konsola Google Play ocenia optymalizację kodu w ramach wskazówek dotyczących jakości aplikacji. Więcej informacji o wymaganiach i limitach dotyczących optymalizacji gier znajdziesz w sekcji Optymalizacja kodu DEX w dokumentacji Android Vitals.

Aby skonfigurować R8 w standardowym projekcie aplikacji na Androida, postępuj zgodnie z instrukcjami w przewodniku Włączanie optymalizacji aplikacji. Aby włączyć zaawansowane ustawienia zmniejszania i optymalizacji, przeczytaj artykuł o używaniu R8 w trybie pełnym. Aby dowiedzieć się, które reguły uniemożliwiają R8 zaciemnianie klas lub usuwanie nieużywanego kodu, użyj analizatora konfiguracji R8.

Jeśli tworzysz grę za pomocą silnika gry, domyślne działanie R8 różni się w zależności od silnika gry i wersji:

Wymagania dotyczące bitmapy

Bitmapy stanowią znaczną część wykorzystania pamięci w nowoczesnych grach o wysokiej jakości. Dane pikseli bitmapy są przechowywane bezpośrednio w niezarządzanej stercie natywnej na Androidzie 8.0 (poziom interfejsu API 26) i nowszych, dlatego nieoptymalne wczytywanie obrazów może spowodować przekroczenie progów pamięci platformy. Sprawdzone metody dotyczące skalowania i buforowania obrazów znajdziesz w artykule Optymalizowanie wykorzystania obrazów.

Monitorowanie wykorzystania pamięci

Aby skutecznie zoptymalizować pamięć gry, musisz najpierw zrozumieć, jak platforma Androida mierzy jej wykorzystanie. Android 17 aktualizuje wskaźnik pamięci, aby śledzić sumę anonimowej pamięci RSS (RssAnon) i nieskompresowanej przestrzeni wymiany (VmSwap), z wyłączeniem pamięci opartej na plikach lub pamięci prywatnej GPU. Z tego przewodnika dowiesz się, jak korzystać z narzędzi na poziomie systemu, takich jak Perfetto i meminfo, wdrażać interfejsy API do diagnostyki, takie jak ProfilingManager i onTrimMemory, oraz wyodrębniać precyzyjne alokacje pamięci w silnikach Unity i Unreal Engine. Dowiedz się, jak dokładnie profilować grę i unikać zacinania się, które jest związane z tradycyjnym odpytywaniem pamięci w czasie działania.

Więcej informacji znajdziesz w artykule Monitorowanie wykorzystania pamięci.

Strategie zmniejszania wykorzystania pamięci

Chociaż silniki gier upraszczają tworzenie gier na wielu platformach, ich domyślna obsługa pamięci może powodować przekroczenie limitów pamięci na poziomie systemu operacyjnego. Na tej stronie znajdziesz praktyczne kroki optymalizacji dostosowane specjalnie do silników Unity i Unreal Engine. Dowiedz się, dlaczego poleganie na onTrimMemory w języku Java może powodować zakleszczenia w Unity i jak zamiast tego używać natywnych wywołań zwrotnych cyklu życia. Poznasz też kluczowe optymalizacje na poziomie zasobów, takie jak używanie kompresji tekstur ASTC 8x8 i konfigurowanie wyładowywania zasobów, dzięki którym gra będzie działać płynnie na wszystkich poziomach sprzętowych.

Więcej informacji znajdziesz w artykule Zmniejszanie wykorzystania pamięci.