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 wydajne wykorzystanie pamięci jest ważne, jak system operacyjny Android zarządza limitami pamięci procesów oraz jakie nowe wskaźniki pamięci są dostępne w Konsoli Google Play, aby pomóc Ci monitorować i poprawiać jakość techniczną gry.

Dlaczego optymalizacja pamięci jest tak ważna

Optymalizacja pamięci gry jest niezbędna, aby utrzymać zainteresowanie graczy, zwiększyć kompatybilność z urządzeniami i spełnić standardy jakości platformy:

  • Zapobieganie uruchomieniu „na zimno” (wrażenia użytkownika i utrzymanie): gdy gracz tymczasowo przełącza się z Twojej 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 Low Memory Killer (LMK) priorytetowo zakończy proces gry, aby odzyskać pamięć RAM na potrzeby zadań na pierwszym planie. Następnym razem, gdy użytkownik wznowi grę, zamiast płynnego i natychmiastowego wznowienia na ciepło gra będzie musiała przejść długie uruchomienie „na zimno” – całkowicie przeładować z pamięci masowej duże zasoby graficzne, dźwięk i pliki binarne silnika gry. Utrzymywanie niskiego wykorzystania pamięci w tle zapobiega tym cichym zakończeniom w tle, zachowując stan użytkownika i zapewniając graczom możliwość natychmiastowego wznowienia sesji. Więcej informacji o działaniu systemowego mechanizmu LMK znajdziesz w przewodniku Android Vitals – Low memory killers.
  • Stabilność ekosystemu i urządzenia: nieefektywne wykorzystanie pamięci i wycieki pamięci pogarszają ogólną kondycję systemu. Gdy pamięć systemowa jest ograniczona, system jest poddawany silnemu obciążeniu, co powoduje spadek liczby klatek na sekundę, 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 doświadczają powolnego zimnego startu i utraty stanu użytkownika, gdy gracze przełączają się między zadaniami.
  • Zakończenia na poziomie platformy: od Androida 17 (poziom interfejsu API 37) system bardziej aktywnie kończy procesy, które zużywają zbyt dużo pamięci. Jeśli wykorzystanie pamięci przez grę jest zbyt wysokie, system operacyjny może nagle zakończyć jej proces bez generowania standardowego śladu stosu.
  • Kompatybilność z urządzeniami: flagowe urządzenia mają od 12 do 16 GB pamięci RAM, ale ogromna część globalnej społeczności graczy korzysta z urządzeń z 4 lub 6 GB pamięci RAM. Prawidłowe zarządzanie pamięcią zapewnia, że gra pozostanie dostępna i responsywna na wszystkich poziomach sprzętu 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ą wiedzieć, jak platforma Android zarządza pamięcią fizyczną i jak mierzy aktywne wykorzystanie pamięci przez grę.

Podstawowe pojęcia dotyczące pamięci w Androidzie

Podstawowe pojęcia dotyczące zarządzania pamięcią na poziomie platformy znajdziesz w oficjalnej dokumentacji Memory Management Overview. 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. Zamiast tego 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 całemu systemowi. Przydziela określone sterty na potrzeby wykonywania maszyn wirtualnych Dalvik lub ART, jednocześnie umożliwiając natywnym środowiskom programistycznym (takim jak silniki gier 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 cyklu życia i dobrowolnie zwalniać niepotrzebne zasoby (takie jak niebuforowane grafiki i bitmapy), aby wspierać kondycję 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 dla użytkownika, określając, które procesy mają być utrzymywane przy życiu, a które mają być zakończone jako pierwsze w warunkach niskiego poziomu pamięci.

Wskaźnik łącznego wykorzystania pamięci

Ogranicznik pamięci w Androidzie 17 na poziomie platformy ocenia zużycie procesów na podstawie łącznego wykorzystania pamięci, a nie łącznego rozmiaru rezydentnego (RSS) ani 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 wiedzieć, co te wskaźniki oznaczają na poziomie systemu. Więcej informacji o tych wskaźnikach, alokacjach pamięci RAM i sposobie obsługi stron wspieranych przez pliki znajdziesz w przewodniku Monitorowanie wykorzystania pamięci.

Ograniczenia pamięci

Aby zachować stabilność systemu i zapewnić, że aplikacje nie będą zużywać nadmiernej ilości zasobów, platforma Android 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ą ścisłymi limitami pamięci dla poszczególnych aplikacji za pomocą Linux cgroup v2, aby zapobiec powodowaniu niestabilności systemu przez poszczególne aplikacje. Więcej informacji o implementacji technicznej znajdziesz w przewodniku AOSP Memory Limiter Guide i artykule Prioritizing Memory Efficiency: Essential Steps for Android 17.

  • Mechanizm: ogranicznik pamięci monitoruje wszystkie procesy aplikacji i dynamicznie przypisuje limity na podstawie stanu cyklu życia procesu:
    • Procesy widoczne (na pierwszym planie): procesy aplikacji, które aktualnie wyświetlają interfejs, powinny mieć większy zestaw roboczy zasobów i mają bardziej liberalny limit.
    • Procesy niewidoczne (w tle lub usługi): procesy aplikacji, które aktywnie działają bez wyświetlania interfejsu, są ograniczone do mniejszego, bardziej restrykcyjnego budżetu.
  • Atrybuty jądra: usługa korzysta z 2 głównych atrybutów:
    • memory.high: miękki limit. Po jego przekroczeniu jądro ogranicza proces i próbuje agresywnie odzyskać pamięć. To odzyskiwanie może spowodować pogorszenie wydajności gry.
    • memory.swap.max: zarządza twardym limitem przestrzeni wymiany lub zRAM, której może używać proces.
  • Działanie po zakończeniu: jeśli proces nadal przydziela anonimową pamięć po przekroczeniu memory.high i wyczerpuje swoją pojemność wymiany, alokacje się nie powiodą, a system operacyjny cicho zakończy proces. To zakończenie jest rejestrowane za pomocą ApplicationExitInfo z przyczyną zakończenia Memory Limiter (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 wskaźniki w Android Vitals w Konsoli Play. Konsola Play śledzi 90. percentyl (P90) anonimowego wykorzystania pamięci RSS + przestrzeń wymiany w sesjach 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 obowiązują w 2 różnych fazach.

Szczegółowe wytyczne i limity pamięci znajdziesz w artykule Android Vitals – Jakie są progi nieprawidłowego działania?.

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 procesy:

  • usługi na pierwszym planie (FGS);
  • zadania priorytetowe;
  • zadania przenoszenia danych inicjowane przez użytkownika;
  • usługi powiązane z systemem lub innymi aplikacjami.

Usługi widoczne są przeznaczone do krytycznych, długotrwałych zadań w tle, dlatego są bardzo podatne na kumulatywne wycieki cyklu życia. Limity pamięci platformy Android traktują ten stan jako nie na pierwszym planie, co oznacza, że jeśli Twoja gra działa w tle, ale nadal uruchamia usługę widoczną, podlega ona bardziej rygorystycznym limitom pamięci w tle lub usługi, które są podane na stronie Centrum pomocy Play. Gry muszą agresywnie usuwać niepotrzebne zasoby podczas przechodzenia z pierwszego planu do stanu usługi widocznej w tle.

Wymagania dotyczące R8

Aby zminimalizować rozmiar kodu bajtowego i zmniejszyć podstawowe obciążenie procesów Java, Konsola Google Play ocenia optymalizację kodu w ramach wskazówek dotyczących jakości aplikacji. Więcej informacji o konfigurowaniu potoku kompilacji znajdziesz w przewodniku Włączanie optymalizacji aplikacji za pomocą R8.

Aby skonfigurować R8 w projekcie, postępuj zgodnie z instrukcjami w przewodniku Włączanie optymalizacji aplikacji za pomocą R8. Aby włączyć zaawansowane ustawienia zmniejszania i optymalizacji, przeczytaj artykuł Korzystanie z R8 w trybie pełnym. Aby sprawdzić, które reguły uniemożliwiają R8 zaciemnianie klas lub usuwanie martwego kodu, użyj narzędzia Use R8 Configuration Analyzer.

Wymagania dotyczące bitmap

Bitmapy stanowią znaczną część wykorzystania pamięci w nowoczesnych grach o wysokiej jakości. Ponieważ dane pikseli bitmap są przechowywane bezpośrednio w niezarządzanej stercie natywnej w Androidzie 8.0 (poziom interfejsu API 26) i nowszych wersjach, nieoptymalne wczytywanie obrazów może spowodować przekroczenie progów pamięci platformy przez procesy. Sprawdzone metody 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 Android 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 wspieranej przez pliki lub prywatnej pamięci GPU. Z tego przewodnika dowiesz się, jak korzystać z narzędzi na poziomie systemu, takich jak Perfetto i meminfo, implementować interfejsy API do diagnostyki, takie jak ProfilingManager i onTrimMemory, oraz wyodrębniać dokładne alokacje pamięci w Unity i Unreal Engine. Dowiedz się, jak dokładnie profilować grę i unikać zacinania się związanego z tradycyjnym odpytywaniem pamięci w czasie działania.

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

Strategie zmniejszania wykorzystania pamięci

Silniki gier upraszczają tworzenie aplikacji na wielu platformach, ale ich domyślna obsługa pamięci może powodować przekroczenie limitów pamięci na poziomie systemu operacyjnego. Na tej stronie znajdziesz praktyczne wskazówki dotyczące optymalizacji dostosowane do Unity i Unreal Engine. Dowiedz się, dlaczego poleganie na onTrimMemory w Javie 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 zwalniania zasobów, aby gra działała płynnie na wszystkich poziomach sprzętu.

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