Optymalizacja pamięci jest kluczowa, aby zapewnić stabilną i wydajną rozgrywkę 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ń działających na pierwszym planie. 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 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 dużemu 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 działające 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.
Pamięć 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 Omówienie zarządzania pamięcią. 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: działając w modelu wieloprocesowym, Android oczekuje, że aplikacje będą dynamicznie monitorować stan cyklu życia i dobrowolnie zwalniać niepotrzebne zasoby (takie jak niebuforowane grafiki i bitmapy), aby zachować 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 z punktu widzenia 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 na poziomie platformy Android 17 ocenia wykorzystanie procesów za pomocą wskaźnika łą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 przez gry limitów platformy, deweloperzy muszą dokładnie wiedzieć, co te wskaźniki oznaczają na poziomie systemu. Więcej informacji o tych wskaźnikach, alokacji pamięci RAM i sposobie obsługi stron wspieranych przez pliki znajdziesz w przewodniku Monitorowanie wykorzystania pamięci w sekcji Omówienie wskaźników RSS i przestrzeni wymiany.
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ą cgroup v2 w Linuksie, aby zapobiec powodowaniu przez poszczególne aplikacje niestabilności w całym systemie. 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:
- Widoczne procesy (pierwszy plan): procesy aplikacji, które obecnie wyświetlają interfejs, powinny mieć większy zestaw roboczy zasobów i mają bardziej liberalny limit.
- Niewidoczne procesy (tło 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.
- Zachowanie po zakończeniu: jeśli proces nadal przydziela anonimową
pamięć po przekroczeniu limitu
memory.highi wyczerpie swoją pojemność wymiany, alokacje się nie powiodą, a system operacyjny cicho zakończy proces. To zakończenie jest rejestrowane za pomocąApplicationExitInfoz przyczyną zakończenia Memory Limiter (dostępne od Androida 17, 26Q4).
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.