Android 17 i nowszy obsługuje demona zarządzania pamięcią (mmd), czyli demona systemowego, który obsługuje konfigurację demona, parametry i bieżące zadania związane z wymianą lub ZRAM.
Tło
Przed wprowadzeniem mmd konfiguracje ZRAM na Androidzie były rozproszone i oferowały ograniczone możliwości dostosowywania. mmd rozwiązuje ten problem, centralizując zarządzanie ZRAM, umożliwiając bardziej zaawansowaną logikę konfiguracji i upraszczając dodawanie nowych funkcji oraz ulepszeń architektury.
mmd zapewnia też wyraźne rozdzielenie zadań między procesem opartym na Javie a system_serverzarządzaniem pamięcią lub wymianą na poziomie jądra.
Architektura i zarządzanie ZRAM
Po zakończeniu rozruchu (czyli gdy sys.boot_completed=1) mmd_setup próbuje skonfigurować ZRAM z określonymi parametrami. Po zakończeniu konfiguracji ZRAM system włącza usługę mmd, która obsługuje bieżące zadania konserwacyjne.
W projekcie mmd operacje konserwacyjne są inicjowane z system_server przez wysyłanie żądań Binder do mmd za pomocą interfejsu IMmd.
mmd wykonuje zadania związane z konserwacją, takie jak zapisywanie zwrotne ZRAM, ponowna kompresja i zapisywanie zwrotne dla poszczególnych procesów, na podstawie własnego wewnętrznego mechanizmu zasad. Zarówno harmonogram z ActivityManagerService, jak i zasady konserwacji ZRAM można skonfigurować za pomocą właściwości systemu.
Integracja serwera systemu (system_server)
Proces system_server oparty na Javie określa, kiedy jest wywoływany proces mmd. Proces ten oddziela globalne czyszczenie pamięci od ukierunkowanych optymalizacji pamięci w poszczególnych aplikacjach.
Normalna konserwacja po przetworzeniu
Globalną konserwacją ZRAM zarządza usługa ActivityManagerService za pomocą funkcji com.android.server.memory.ZramMaintenance.

Rysunek 1. Przepływ planowania konserwacji ZRAM.
- Silnik planowania:
ZramMaintenancerejestruje okresowe zadanie w tle w usłudzeJobSchedulerAndroida. - Ograniczenia zadania: aby zapobiec przeskokom interfejsu użytkownika na pierwszym planie lub rywalizacji o procesor, zadanie jest wyraźnie skonfigurowane za pomocą
setRequiresDeviceIdle(true)isetRequiresBatteryNotLow(true). - Uruchamianie przez Binder: gdy algorytm szeregowania uruchamia
onStartJob(),system_server, wywołujemmd.doZramMaintenanceAsync(). Jest to jednokierunkowe wywołanie asynchroniczne interfejsu Binder. Funkcjasystem_servernie blokuje oczekiwania na zakończenie skanowania konserwacyjnego.mmdumieszcza to w kolejce wątku roboczego w tle, aby wykonać ponowną kompresję i zapisanie zwrotne w kolejności.
Zapisywanie zwrotne na poziomie procesu
Ukierunkowane usuwanie pamięci z poszczególnych procesów jest zarządzane przez usługę ActivityManagerService za pomocą parametru
com.android.server.am.CachedAppOptimizer.

Rysunek 2. Przepływ zapisu zwrotnego mmd w przypadku poszczególnych procesów.
Gdy proces przechodzi w stan buforowania w tle, ActivityManager przeprowadza kompresję pamięci. Jeśli błąd LMK (low memory kill) procesu byłby widoczny dla użytkownika, tzn. proces hostuje aktywność, a zapisywanie zwrotne ZRAM na proces spowodowałoby zmniejszenie wykorzystania pamięci procesu do niemal zera, system wykonuje te czynności:
- Po kompresji
CachedAppOptimizerwysyła opóźnioną wiadomość (ZRAM_WRITEBACK_MSG) do wewnętrznego modułu obsługi kompresji (opóźnioną omZramWritebackWaitSeconds). - Po upływie czasu opóźnienia ActivityManager otwiera bezpieczny deskryptor pliku procesu
pidfd. - Serwer systemowy wywołuje
mmd.asyncWritebackProcessZramMemory(pfd, callback). mmdwykonuje ioctl zapisu zwrotnego dla poszczególnych procesów i przesyła raport za pomocąIMmdProcessWritebackCallback. Jeśli się to uda, ActivityManager oznaczy rekord procesu (setIsZramWrittenBack(app, true)), aby zwiększyć jegooom_score_adj, i zarejestruje dane wFrameworkStatsLog.ZRAM_WRITEBACK_EVENT.
Pobieranie z wyprzedzeniem dla każdego procesu
Gdy użytkownik ponownie uruchomi wcześniej buforowaną aplikację (odmrożoną z powodu UNFREEZE_REASON_ACTIVITY), ActivityManager minimalizuje opóźnienie uruchamiania aplikacji spowodowane przez poważne błędy strony z pamięci zapasowej:
CachedAppOptimizerprzechwytuje zdarzenie odblokowania i wywołujeprefetchZram(app).- Serwer systemowy wysyła
pidfdaplikacji za pomocą interfejsu Binder, używającmmd.asyncPrefetchProcessZramMemory(pfd).mmdwysyła ioctl, instruując jądro, aby asynchronicznie pobierało wstępnie zamienione strony z powrotem do pamięci RAM, podczas gdy główny wątek interfejsu aplikacji jest inicjowany.ZRAM_ANDROID_IOC_PROCESS_PREFETCH
Omówienie zadań konserwacyjnych i zadań przetwarzania końcowego
W tej sekcji opisujemy operacje konserwacyjne w tle i zadania przetwarzania końcowego, które mmd wykonuje w celu optymalizacji przestrzeni wymiany i pamięci systemowej.
Konserwacja w mmd
W mmd konserwacja oznacza zaplanowane, działające w tle procesy konserwacyjne, które optymalizują wykorzystanie przestrzeni wymiany i pamięci fizycznej bez wpływu na wydajność aktywnych użytkowników. Zamiast wykonywać ciągłe, synchroniczne skanowanie (które powodowałoby częste wybudzanie procesora i zacinanie się interfejsu), konserwacja jest przeprowadzana asynchronicznie:
system_serverokresowo wysyładoZramMaintenanceAsync()przez Binder.mmdumieszcza żądanie w kolejce zadań w tleLowPrioWorkItem::ZramMaintenance.W
mmdjest jeden wątek roboczy, który zarządza kolejką o wysokim priorytecie i kolejką o niskim priorytecie. Elementy robocze o wysokim priorytecie (np. wstępne pobieranie danych dla poszczególnych procesów) są przetwarzane w pierwszej kolejności i mogą przerywać przetwarzanie elementów roboczych o niskim priorytecie. Konserwacja i zapisywanie zwrotne w przypadku poszczególnych procesów działają jako zadania o niskim priorytecie. Po wywołaniu wątek instancji roboczej wykonuje kolejno 2 podstawowe operacje konserwacyjne:Ponowna kompresja ZRAM: skanuje istniejące strony wymiany i ponownie kompresuje nieużywane strony za pomocą algorytmu kompresji wtórnej o wyższym współczynniku, np.
zstd.Zapisywanie do ZRAM: skanuje nieużywane strony i całkowicie usuwa je z pamięci RAM do pamięci flash, która jest urządzeniem pętli z pliku na
/data.
Zadania przetwarzania końcowego w pamięci ZRAM
W module ZRAM jądra Linuksa i architekturze mmd zadania przetwarzania końcowego to asynchroniczne przekształcenia stosowane do stron pamięci po ich zamianie przez standardowe ścieżki odzyskiwania jądra (kswapd lub kompresja).
Gdy strona jest początkowo zamieniana, system priorytetowo traktuje szybkość: używa szybkiego podstawowego algorytmu kompresji (np. lz4) i przechowuje skompresowaną stronę w pamięci RAM. Z czasem jednak wiele stron zamienionych na pamięć podręczną staje się nieaktywnych lub bezczynnych, np. aplikacje w pamięci podręcznej w tle, które nie są wznawiane przez wiele godzin. Pozostawianie nieużywanych stron w szybkiej, lekko skompresowanej pamięci ZRAM jest nieefektywne.
Potok przetwarzania końcowego
mmd wdraża wieloetapowy cykl życia przetwarzania końcowego, aby zoptymalizować te strony:

Rysunek 3. mmd cykl życia strony.
Etap 1. Wstępna wymiana (szybka kompresja): pamięć jest najpierw odzyskiwana za pomocą kswapd lub kompresji aplikacji. Zwykle pierwsze odzyskiwanie jest wykonywane przy użyciu szybkiego algorytmu kompresji, takiego jak
lz4, a zawartość jest przechowywana w pamięci RAM.Etap 2. Oznaczanie stanu bezczynności (starzenie i śledzenie):
mmdśledzenie stanu bezczynności uzyskuje dostęp do śledzenia pamięci jądra (CONFIG_ZRAM_TRACK_ENTRY_ACTIME) lub używa własnego znacznika bezczynności oprogramowania, aby śledzić, jak długo strony pozostają nietknięte.Etap 3. Przetwarzanie końcowe 1 – ponowna kompresja (odzyskiwanie pamięci): strony, które osiągną wiek bezczynności ponownej kompresji (
min_idle_seconds–max_idle_seconds), są ponownie kompresowane.mmdzapisuje w/sys/block/zram0/recompress, aby poinstruować jądro, aby zdekompresowało stronęlz4i ponownie skompresowało ją za pomocązstd. Pozwala to odzyskać fizyczną pamięć RAM bez zużywania pamięci flash.Etap 4. Przetwarzanie końcowe 2 – zapisywanie zwrotne (usuwanie z pamięci flash): jeśli obciążenie pamięci się utrzymuje, a strony osiągną wiek bezczynności zapisu zwrotnego (zwykle 20 godzin lub więcej),
mmdwywołuje zapis zwrotny.mmdzapisuje/sys/block/zram0/idlei/sys/block/zram0/writeback, aby całkowicie usunąć skompresowaną stronę z pamięci RAM do pamięci flash.
Konfiguracja ZRAM
mmd wczytuje i przetwarza te właściwości konfiguracji ZRAM:
| Właściwość | Użyj | Domyślny |
|---|---|---|
mmd.zram.enabled |
Czy konfiguracja mmd ZRAM jest włączona. |
false |
mmd.zram.num_devices |
Liczba urządzeń ZRAM do skonfigurowania. W przypadku liczby N urządzenia zram0–zram<N-1> muszą być obecne, zanim system ustawi wartość sys.boot_completed=1.
Właściwości na liście urządzeń z pamięcią ZRAM można konfigurować na poziomie poszczególnych urządzeń.
|
1 |
mmd.zram.device_priority |
Wartości priorytetu do przekazania podczas wywoływania funkcji swapon. |
Nie ustawiono |
mmd.zram.comp_algorithm |
Algorytm kompresji ZRAM. Jeśli nie zostanie określony, używany jest domyślny algorytm kompresji jądra. | Nie ustawiono |
mmd.zram.size |
Rozmiar urządzenia ZRAM w bajtach lub procent rozmiaru pamięci RAM urządzenia, np.75%.
|
50% |
mmd.zram.writeback.enabled |
Określa, czy włączyć zapisywanie zwrotne ZRAM. | false |
mmd.zram.writeback.device_size |
Rozmiar urządzenia zapisu zwrotnego w bajtach lub jako procent partycji danych. Rzeczywisty rozmiar urządzenia można dostosować do dostępnego miejsca na partycji danych. | 1073741824 (1 GiB) |
mmd.zram.writeback.min_free_space_mib |
Minimalna ilość wolnego miejsca w MiB, która musi być dostępna po skonfigurowaniu urządzenia zapisu zwrotnego. | 1536 (1,5 GiB) |
mmd.zram.writeback.use_nr_tags_prop |
Gdy true używa wartości w mmd.zram.writeback.nr_tags do skonfigurowania głębokości kolejki urządzenia pętli obsługującego zapisywanie zwrotne ZRAM. Jest to obejście w sytuacjach, w których nie można skonfigurować zasad SELinux dostawcy, aby umożliwić mmd bezpośrednie odczytywanie nr_tags urządzenia blokowego obsługującego /data.
|
false |
mmd.zram.writeback.nr_tags |
Zobacz mmd.zram.writeback.use_nr_tags_prop. |
Nie ustawiono |
mmd.zram.recompression.enabled |
Określa, czy włączyć funkcję ponownej kompresji ZRAM. | false |
mmd.zram.recompression.algorithm |
Dodatkowy algorytm ponownej kompresji ZRAM. | zstd |
Właściwości urządzenia ZRAM
Gdy wartość mmd.zram.num_devices jest większa niż 1, poszczególne właściwości można opcjonalnie skonfigurować dla każdego urządzenia ZRAM, ustawiając właściwość na wartość rozdzieloną przecinkami, która zawiera dokładnie mmd.zram.num_devices elementów.
Obejmują one:
mmd.zram.sizemmd.zram.comp_algorithmmmd.zram.device_prioritymmd.zram.recompression.enabledmmd.zram.recompression.huge_idle.enabledmmd.zram.recompression.idle.enabledmmd.zram.recompression.huge.enabledmmd.zram.recompression.threshold_bytesmmd.zram.recompression.algorithmmmd.zram.writeback.device_sizemmd.zram.writeback.huge_idle.enabledmmd.zram.writeback.idle.enabledmmd.zram.writeback.huge.enabled
Wycofanie istniejącej konfiguracji ZRAM
Chociaż swapon_all jest nadal dostępny na Androidzie do konfigurowania ZRAM i przestrzeni wymiany opartej na dysku, mmd jest preferowanym podejściem do zarządzania ZRAM, ponieważ ułatwia konfigurację i zapewnia zaawansowane funkcje, takie jak ponowna kompresja ZRAM.
Gdy mmdkonfiguracja ZRAM jest włączona przez mmd.zram.enabled:
- Konfiguracja ZRAM w
swapon_allstaje się bezczynna. - Istniejące konfiguracje ZRAM, takie jak
config_zramWritebackw pliku nakładkiconfig.xmliro.zram.*właściwości systemu zapisu zwrotnego, są ignorowane.
Ustawienia konserwacji ZRAM
Konserwacja ZRAM powinna działać od razu po wyjęciu z pudełka. Możesz ją dodatkowo dostroić za pomocą właściwości systemu w tej sekcji.
Planowanie konserwacji ZRAM
Te właściwości określają, jak i kiedy zadania konserwacyjne ZRAM są planowane przez system_server.
| Właściwość | Użyj | Domyślny |
|---|---|---|
mm.zram.maintenance.first_delay_seconds |
Opóźnienie przed rozpoczęciem pierwszej konserwacji ZRAM. | 3600 (1 godzina) |
mm.zram.maintenance.periodic_delay_seconds |
Opóźnienie między kolejnymi harmonogramami konserwacji ZRAM. | 3600 (1 godzina) |
mm.zram.maintenance.require_device_idle |
Określa, czy konserwację ZRAM należy inicjować tylko wtedy, gdy urządzenie jest bezczynne. | true |
mm.zram.maintenance.require_battery_not_low |
Określa, czy przed rozpoczęciem konserwacji ZRAM bateria nie powinna być rozładowana. | true |
Zasady zapisu zwrotnego ZRAM
Te parametry określają, kiedy i jaki typ pamięci jest zapisywany na urządzeniu zapasowym:
| Właściwość | Użyj | Domyślny |
|---|---|---|
mmd.zram.writeback.backoff_seconds |
Czas oczekiwania od ostatniej operacji zapisu zwrotnego. | 600 (10 minut) |
mmd.zram.writeback.min_idle_seconds |
W połączeniu z mmd.zram.writeback.max_idle_seconds służy do obliczania czasu bezczynności strony, aby kwalifikowała się ona do zapisu zwrotnego na podstawie ułamka wykorzystania pamięci. Obliczony czas bezczynności jest interpolowany wykładniczo między tymi dwoma parametrami, aby zminimalizować obciążenie, gdy nie występuje brak pamięci.
|
72000 (20 godzin) |
mmd.zram.writeback.max_idle_seconds |
Maksymalna liczba sekund używana do dynamicznego obliczania wieku nieaktywnej strony na podstawie wykorzystania pamięci. | 90000 (25 godzin) |
mmd.zram.writeback.huge.enabled |
Czy włączyć zapisywanie zmian na stronie HUGE. |
false |
mmd.zram.writeback.idle.enabled |
Czy włączyć zapisywanie zmian na stronie IDLE. |
true |
mmd.zram.writeback.huge_idle.enabled |
Czy włączyć zapisywanie zmian na stronie HUGE_IDLE. |
true |
mmd.zram.writeback.min_bytes |
Minimalna liczba bajtów do zapisania w jednej rundzie zapisu zwrotnego w stanie bezczynności. | 5242880 (5 MiB) |
mmd.zram.writeback.max_bytes |
Maksymalna liczba bajtów do zapisania w jednej rundzie zapisu zwrotnego w stanie bezczynności. | 314572800 (300 MiB) |
mmd.zram.writeback.max_bytes_per_day |
Maksymalna liczba bajtów do zapisania w ciągu 24 godzin. | 25769803776 (24 GiB) |
mmd.zram.writeback.limit.enabled |
Określa, czy włączyć rozliczanie limitu budżetu dziennego zapisu zwrotnego. | true |
Zasady ponownej kompresji ZRAM
Te parametry określają, kiedy i jakiego typu pamięć jest ponownie kompresowana:
| Właściwość | Użyj | Domyślny |
|---|---|---|
mmd.zram.recompression.backoff_seconds |
Czas do ponowienia od ostatniej ponownej kompresji. | 1800 (30 minut) |
mmd.zram.recompression.min_idle_seconds |
W połączeniu z mmd.zram.recompression.max_idle_seconds służy do obliczania czasu bezczynności strony, aby kwalifikowała się ona do ponownej kompresji na podstawie ułamka wykorzystania pamięci. Obliczony czas bezczynności jest interpolowany wykładniczo między tymi dwoma parametrami, aby zminimalizować obciążenie, gdy nie występuje brak pamięci.
|
7200 (2 godziny) |
mmd.zram.recompression.max_idle_seconds |
Maksymalna liczba sekund używana do dynamicznego obliczania wieku nieaktywnej strony. | 14400 (4 godziny) |
mmd.zram.recompression.threshold_bytes |
Minimalny rozmiar w bajtach stron ZRAM, które są brane pod uwagę podczas ponownej kompresji. | 1024 (1 KiB) |
mmd.zram.recompression.huge.enabled |
Czy włączyć ponowną kompresję strony HUGE. |
true |
mmd.zram.recompression.idle.enabled |
Czy włączyć ponowną kompresję strony IDLE. |
true |
mmd.zram.recompression.huge_idle.enabled |
Czy włączyć ponowną kompresję strony HUGE_IDLE. |
true |
Śledzenie nieużywanych stron ZRAM
mmd Konserwacja ZRAM oznacza strony ZRAM jako nieaktywne na podstawie tego, jak długo nie były używane. Ta funkcja wymaga włączenia konfiguracji jądra CONFIG_ZRAM_TRACK_ENTRY_ACTIME lub CONFIG_ZRAM_MEMORY_TRACKING. CONFIG_ZRAM_TRACK_ENTRY_ACTIME jest domyślnie włączona w przypadku jąder GKI w wersji 6.18 i nowszych. W starszych wersjach jądra wiąże się to z dodatkowym obciążeniem pamięci i nie jest domyślnie włączone.
Jeśli konfiguracja jądra nie jest włączona, mmd konserwacja ZRAM wraca do
logiki zastępczej oprogramowania, aby śledzić nieużywane strony ZRAM:
Oznaczanie wszystkich stron ZRAM jako nieużywanych podczas uruchamiania
mmd.Pomiń kolejne czynności konserwacyjne ZRAM, dopóki nie upłynie wymagany okres wycofania.
Zapisywanie zwrotne lub ponowne kompresowanie nieużywanych stron w pamięci ZRAM. Jeśli z powodu limitów zapisu pozostały nieaktywne strony,
mmdkontynuuje zapisywanie stron podczas następnej konserwacji bez oznaczania nowych stron jako nieaktywnych (pomijając krok 4).Jeśli wszystkie nieaktywne strony zostaną zapisane, ponownie oznacz wszystkie strony ZRAM jako nieaktywne i wróć do kroku 2. Jeśli zapisywanie zwrotne ZRAM jest wyłączone,
mmdoznacza wszystkie strony ZRAM jako nieaktywne, gdy po upływie czasu nieaktywności ponownej kompresji następuje ponowna kompresja ZRAM.
Wskazówki dotyczące rozwiązywania problemów i weryfikacji
Aby zweryfikować i zdiagnozować działanie mmd i ZRAM, wykonaj te czynności weryfikacyjne i procedury rozwiązywania problemów.
Weryfikowanie konfiguracji ZRAM
Aby sprawdzić, czy mmd prawidłowo skonfigurował ZRAM podczas uruchamiania:
Sprawdź aktywny algorytm kompresji i rozmiar dysku:
cat /sys/block/zram0/comp_algorithm cat /sys/block/zram0/disksizeSprawdź
mmdwłaściwości systemu i stan uruchomionej usługi:getprop | grep mmd.zram dumpsys -l | grep mmd
Weryfikacja konserwacji i zapisu zwrotnego ZRAM
Sprawdź, czy zadania konserwacyjne związane z zapisywaniem zwrotnym i ponowną kompresją ZRAM działają prawidłowo:
Sprawdź stan urządzenia blokowego:
cat /sys/block/zram0/bd_statSprawdź skuteczność ponownej kompresji, monitorując
/sys/block/zram0/mm_stat. Zmiany rozmiarów skompresowanych danych powinny pojawić się po cyklach konserwacji.
Weryfikowanie zapisu zwrotnego w przypadku poszczególnych procesów
Aby sprawdzić, czy zapisywanie zwrotne w przypadku poszczególnych procesów działa prawidłowo, możesz użyć tych metod:
- Sprawdź
adb logcat -s mmd, aby znaleźć logi udanych zapisów zwrotnych lub diagnostykę błędów.
Typowe problemy i diagnostyka
Poniżej znajdziesz typowe sytuacje, w których użytkownik może napotkać błędy:
WritebackDailyLimitExceeded: ten błąd oznacza, że osiągnięto limitmmd.zram.writeback.max_bytes_per_day. W takiej sytuacjimmdwstrzymuje zapisywanie zwrotne w stanie bezczynności do czasu, aż przesunie się 24-godzinne okno ruchome.Process prefetch or writeback failed: ten błąd może pojawić się w logcat, gdy nie powiedzie się wywołanie ioctl. Częste przyczyny:EBADFlubESRCH: proces docelowy zakończył się, zanimmmdmógł wysłaćpidfddo jądra.ENOSPC: partycja pamięci zapasowej jest pełna lub kolejka urządzenia pętli jest wyczerpana.
- Nie skonfigurowano ZRAM: jeśli
mmdnie uda się skonfigurować ZRAM podczas uruchamiania, może to być spowodowane tym, że starsze skrypty inicjująceswapon_alllub skrypty dostawcy zablokowały/dev/block/zram0, zanimmmdmogło się wykonać.