Jitter to losowe zachowanie systemu, które uniemożliwia wykonanie zauważalnej pracy. Na tej stronie opisano, jak identyfikować i rozwiązywać problemy związane z jitterem.
Opóźnienie harmonogramu wątków aplikacji
Opóźnienie harmonogramu jest najbardziej oczywistym objawem fluktuacji: proces, który powinien zostać uruchomiony, staje się możliwy do uruchomienia, ale nie działa przez dłuższy czas. Znaczenie opóźnienia różni się w zależności od kontekstu. Na przykład:
- Losowy wątek pomocniczy w aplikacji może prawdopodobnie zostać opóźniony o wiele milisekund bez problemu.
- Wątek interfejsu użytkownika aplikacji może tolerować wahania wynoszące 1–2 ms.
- Wątki sterownika działające jako SCHED_FIFO mogą powodować problemy, jeśli przed uruchomieniem można je uruchomić przez 500us.
Czasy działania można rozpoznać w systrace po niebieskim pasku poprzedzającym działający segment wątku. Czas działania można również określić na podstawie czasu pomiędzy zdarzeniem sched_wakeup
dla wątku a zdarzeniem sched_switch
sygnalizującym rozpoczęcie wykonywania wątku.
Wątki, które trwają zbyt długo
Wątki interfejsu użytkownika aplikacji, które działają zbyt długo, mogą powodować problemy. Wątki niższego poziomu o długim czasie działania mają zazwyczaj różne przyczyny, ale próba wypchnięcia czasu działania wątku interfejsu użytkownika do zera może wymagać naprawienia niektórych z tych samych problemów, które powodują, że wątki niższego poziomu mają długi czas działania. Aby złagodzić opóźnienia:
- Użyj zestawów procesorów zgodnie z opisem w sekcji Ograniczanie temperatury .
- Zwiększ wartość CONFIG_HZ.
- Historycznie rzecz biorąc, wartość była ustawiana na 100 na platformach arm i arm64. Jest to jednak przypadek historyczny i nie jest to dobra wartość do wykorzystania w przypadku urządzeń interaktywnych. CONFIG_HZ=100 oznacza, że chwila trwa 10 ms, co oznacza, że równoważenie obciążenia pomiędzy procesorami może zająć 20 ms (dwa migi). Może to znacząco przyczynić się do szarpnięcia w obciążonym systemie.
- Najnowsze urządzenia (Nexus 5X, Nexus 6P, Pixel i Pixel XL) są dostarczane z ustawieniem CONFIG_HZ=300. Powinno to wiązać się z znikomym kosztem energii, a jednocześnie znacznie poprawić czas pracy. Jeśli po zmianie CONFIG_HZ zauważysz znaczny wzrost zużycia energii lub problemy z wydajnością, prawdopodobnie jeden ze sterowników używa licznika czasu opartego na surowych jiffach zamiast milisekundach i konwertuje na jiffi. Zwykle jest to łatwe rozwiązanie (zobacz łatkę , która rozwiązała problemy z zegarem kgsl na Nexusie 5X i 6P podczas konwersji do CONFIG_HZ=300).
- Na koniec eksperymentowaliśmy z CONFIG_HZ=1000 na Nexusie/Pixelu i odkryliśmy, że zapewnia zauważalną wydajność i redukcję mocy ze względu na zmniejszone obciążenie RCU.
Tylko dzięki tym dwóm zmianom urządzenie powinno wyglądać znacznie lepiej pod względem czasu działania wątku interfejsu użytkownika pod obciążeniem.
Używanie sys.use_fifo_ui
Możesz spróbować wyzerować czas działania wątku interfejsu użytkownika, ustawiając właściwość sys.use_fifo_ui
na 1.
Ostrzeżenie : nie używaj tej opcji w heterogenicznych konfiguracjach procesora, chyba że masz program planujący RT uwzględniający pojemność. W tej chwili ŻADEN OBECNIE HARMONOGRAM WYSYŁKI RT NIE JEST ŚWIETNY O WYDAJNOŚCI . Pracujemy nad jedną dla EAS, ale nie jest ona jeszcze dostępna. Domyślny harmonogram RT opiera się wyłącznie na priorytetach RT i tym, czy procesor ma już wątek RT o równym lub wyższym priorytecie.
W rezultacie domyślny harmonogram RT z radością przeniesie stosunkowo długo działający wątek interfejsu użytkownika z dużego rdzenia o wysokiej częstotliwości do małego rdzenia z minimalną częstotliwością, jeśli zdarzy się, że wątek FIFO o wyższym priorytecie obudzi się na tym samym dużym rdzeniu. Spowoduje to znaczny spadek wydajności . Ponieważ ta opcja nie została jeszcze użyta na dostarczonym urządzeniu z Androidem, jeśli chcesz z niej skorzystać, skontaktuj się z zespołem ds. wydajności Androida, aby pomógł Ci ją zweryfikować.
Gdy sys.use_fifo_ui
jest włączone, ActivityManager śledzi wątek interfejsu użytkownika i RenderThread (dwa wątki najbardziej krytyczne dla interfejsu użytkownika) najwyższej aplikacji i tworzy te wątki SCHED_FIFO zamiast SCHED_OTHER. To skutecznie eliminuje drgania z interfejsu użytkownika i RenderThreads; ślady, które zebraliśmy przy włączonej tej opcji, pokazują możliwe do wykonania czasy rzędu mikrosekund, a nie milisekund.
Ponieważ jednak moduł równoważenia obciążenia RT nie uwzględniał pojemności, wydajność uruchamiania aplikacji spadła o 30%, ponieważ wątek interfejsu użytkownika odpowiedzialny za uruchamianie aplikacji zostałby przeniesiony ze złotego rdzenia Kryo 2,1 GHz na srebrny rdzeń Kryo 1,5 GHz . Dzięki modułowi równoważenia obciążenia RT uwzględniającemu pojemność, w wielu naszych testach porównawczych interfejsu użytkownika obserwujemy równoważną wydajność w operacjach masowych i 10–15% redukcję czasu klatek 95. i 99. percentyla.
Przerwać ruch
Ponieważ platformy ARM domyślnie dostarczają przerwania tylko do procesora 0, zalecamy użycie modułu równoważącego IRQ (irqbalance lub msm_irqbalance na platformach Qualcomm).
Podczas opracowywania Pixela zaobserwowaliśmy szarpanie, które można bezpośrednio przypisać przeciążaniu procesora 0 przerwaniami. Na przykład, jeśli wątek mdss_fb0
został zaplanowany na CPU 0, prawdopodobieństwo szarpnięcia było znacznie większe z powodu przerwania wyzwalanego przez wyświetlacz niemal bezpośrednio przed skanowaniem. mdss_fb0
byłby w trakcie własnej pracy z bardzo napiętym terminem realizacji i wtedy straciłby trochę czasu na obsługę przerwań MDSS. Początkowo próbowaliśmy to naprawić, ustawiając powinowactwo procesora wątku mdss_fb0 na procesory 1-3, aby uniknąć rywalizacji z przerwaniem, ale potem zdaliśmy sobie sprawę, że nie włączyliśmy jeszcze funkcji msm_irqbalance. Po włączeniu msm_irqbalance, jank został zauważalnie poprawiony, nawet gdy zarówno przerwanie mdss_fb0, jak i przerwanie MDSS znajdowały się na tym samym procesorze, ze względu na zmniejszoną rywalizację ze strony innych przerwań.
Można to zidentyfikować w systrace, patrząc na sekcję sched oraz sekcję irq. Sekcja sched pokazuje, co zostało zaplanowane, ale nakładający się obszar w sekcji irq oznacza, że w tym czasie działa przerwanie zamiast normalnie zaplanowanego procesu. Jeśli zauważysz, że przerwanie zajmuje znaczną część czasu, dostępne opcje obejmują:
- Spraw, aby obsługa przerwań była szybsza.
- Przede wszystkim zapobiegaj wystąpieniu przerwania.
- Zmień częstotliwość przerwania tak, aby była niezgodna z inną regularną pracą, w którą może zakłócać (jeśli jest to zwykłe przerwanie).
- Ustaw bezpośrednio powinowactwo procesora dla przerwania i zapobiegnij jego zrównoważeniu.
- Ustaw powinowactwo procesora wątku, z którym zakłóca się przerwanie, aby uniknąć przerwania.
- Polegaj na module równoważenia przerwań, aby przenieść przerwanie do mniej obciążonego procesora.
Ustawienie powinowactwa procesora nie jest generalnie zalecane, ale może być przydatne w określonych przypadkach. Ogólnie rzecz biorąc, zbyt trudno jest przewidzieć stan systemu dla większości typowych przerwań, ale jeśli istnieje bardzo specyficzny zestaw warunków wyzwalających określone przerwania, gdy system jest bardziej ograniczony niż normalnie (np. VR), jawne powinowactwo procesora może być dobrym rozwiązaniem.
Długie softy
Kiedy softirq jest uruchomiony, wyłącza wywłaszczanie. softirqs można także uruchomić w wielu miejscach jądra i uruchomić wewnątrz procesu użytkownika. Jeśli aktywność softirq jest wystarczająca, procesy użytkownika przestaną uruchamiać softirqs, a ksoftirqd obudzi się, aby uruchomić softirqs i zrównoważyć obciążenie. Zwykle jest to w porządku. Jednak pojedynczy, bardzo długi softirq może siać spustoszenie w systemie.
softirqs są widoczne w sekcji IRQ śledzenia, więc łatwo je wykryć, jeśli problem uda się odtworzyć podczas śledzenia. Ponieważ softirq może działać w procesie użytkownika, zły softirq może również bez oczywistego powodu objawiać się jako dodatkowy czas działania w procesie użytkownika. Jeśli to widzisz, sprawdź sekcję irq, aby zobaczyć, czy przyczyną jest softirqs.
Sterowniki pozostawiające wywłaszczanie lub przerwania IRQ wyłączone zbyt długo
Wyłączenie wywłaszczania lub przerwań na zbyt długo (dziesiątki milisekund) powoduje jank. Zwykle jank objawia się tym, że wątek staje się możliwy do uruchomienia, ale nie działa na określonym procesorze, nawet jeśli uruchamialny wątek ma znacznie wyższy priorytet (lub SCHED_FIFO) niż inny wątek.
Niektóre wytyczne:
- Jeśli wykonywalny wątek to SCHED_FIFO, a działający wątek to SCHED_OTHER, działający wątek ma wyłączone wywłaszczanie lub przerwania.
- Jeśli wykonywalny wątek ma znacznie wyższy priorytet (100) niż działający wątek (120), działający wątek prawdopodobnie ma wywłaszczanie lub przerwania wyłączone, jeśli wykonywalny wątek nie zostanie uruchomiony w ciągu dwóch chwil.
- Jeśli wykonywalny wątek i działający wątek mają ten sam priorytet, działający wątek prawdopodobnie ma wywłaszczanie lub przerwania wyłączone, jeśli wykonywalny wątek nie zostanie uruchomiony w ciągu 20 ms.
Należy pamiętać, że uruchomienie procedury obsługi przerwań uniemożliwia obsługę innych przerwań, co również wyłącza wywłaszczanie.
Inną opcją identyfikacji regionów naruszających zasady jest użycie znacznika preemptirqsoff (zobacz Korzystanie z dynamicznego ftrace ). Ten moduł śledzący może zapewnić znacznie lepszy wgląd w podstawową przyczynę obszaru nieprzerywalnego (np. nazwy funkcji), ale jego włączenie wymaga bardziej inwazyjnej pracy. Chociaż może to mieć większy wpływ na wydajność, zdecydowanie warto spróbować.
Nieprawidłowe użycie kolejek roboczych
Procedury obsługi przerwań często muszą wykonywać pracę, która może działać poza kontekstem przerwań, umożliwiając przeniesienie pracy do różnych wątków w jądrze. Twórca sterownika może zauważyć, że jądro posiada bardzo wygodną, ogólnosystemową funkcję zadań asynchronicznych zwaną kolejkami roboczymi i może ją wykorzystać do pracy związanej z przerwaniami.
Jednak kolejki robocze prawie zawsze są złym rozwiązaniem tego problemu, ponieważ zawsze są to SCHED_OTHER. Wiele przerwań sprzętowych znajduje się na krytycznej ścieżce wydajności i należy je natychmiast uruchomić. Kolejki robocze nie mają gwarancji, kiedy zostaną uruchomione. Za każdym razem, gdy widzieliśmy kolejkę roboczą na krytycznej ścieżce wydajności, było to źródłem sporadycznych szarpnięć, niezależnie od urządzenia. W przypadku Pixela z flagowym procesorem zaobserwowaliśmy, że pojedyncza kolejka robocza może być opóźniona do 7 ms, jeśli urządzenie było obciążone, w zależności od zachowania harmonogramu i innych czynników działających w systemie.
Zamiast kolejki roboczej sterowniki, które muszą obsługiwać pracę przypominającą przerwania w oddzielnym wątku, powinny utworzyć własny kwątek SCHED_FIFO. Aby uzyskać pomoc dotyczącą funkcji kthread_work, zapoznaj się z tą poprawką .
Konflikt dotyczący blokady struktury
Konflikt dotyczący blokady struktury może być źródłem jank lub innych problemów z wydajnością. Zwykle jest to spowodowane blokadą usługi ActivityManagerService, ale można ją zaobserwować również w przypadku innych blokad. Na przykład blokada usługi PowerManagerService może mieć wpływ na wydajność ekranu. Jeśli widzisz to na swoim urządzeniu, nie ma dobrego rozwiązania, ponieważ można to ulepszyć jedynie poprzez ulepszenia architektoniczne frameworka. Jeśli jednak modyfikujesz kod działający wewnątrz serwera systemowego, niezwykle ważne jest unikanie utrzymywania blokad przez długi czas, zwłaszcza blokady ActivityManagerService.
Spór o blokadę segregatora
Historycznie rzecz biorąc, segregator miał jedną globalną blokadę. Jeśli wątek wykonujący transakcję segregatora został wywłaszczony podczas utrzymywania blokady, żaden inny wątek nie może wykonać transakcji segregatora, dopóki oryginalny wątek nie zwolni blokady. To jest złe; rywalizacja o spoiwo może blokować wszystko w systemie, w tym wysyłanie aktualizacji interfejsu użytkownika na wyświetlacz (wątki interfejsu użytkownika komunikują się z SurfaceFlingerem za pośrednictwem spoiwa).
Android 6.0 zawierał kilka poprawek poprawiających to zachowanie, wyłączając wywłaszczanie podczas trzymania blokady segregatora. Było to bezpieczne tylko dlatego, że blokadę segregatora należało przytrzymać przez kilka mikrosekund rzeczywistego czasu działania. To radykalnie poprawiło wydajność w sytuacjach bezspornych i zapobiegło rywalizacji, uniemożliwiając większość przełączeń programu planującego, gdy blokada segregatora była utrzymana. Jednakże wywłaszczania nie można było wyłączyć przez cały czas działania blokady segregatora, co oznacza, że włączono wywłaszczanie dla funkcji, które mogły uśpić (takich jak kopia_od_użytkownika), co mogło spowodować takie samo wywłaszczenie jak w przypadku oryginalnym. Kiedy wysłaliśmy łatki w górę rzeki, natychmiast powiedzieli nam, że to najgorszy pomysł w historii. (Zgodziliśmy się z nimi, ale nie mogliśmy również kwestionować skuteczności plastrów w zapobieganiu Jankowi.)
fd rywalizacja w procesie
Jest to rzadkie zjawisko. Prawdopodobnie Twój żart nie jest z tego powodu spowodowany.
To powiedziawszy, jeśli w procesie istnieje wiele wątków piszących ten sam fd, można zobaczyć rywalizację na tym fd, jednak jedyny raz, kiedy widzieliśmy to podczas uruchamiania Pixela, miał miejsce podczas testu, podczas którego wątki o niskim priorytecie próbowały zająć cały procesor czas, gdy w tym samym procesie działał pojedynczy wątek o wysokim priorytecie. Wszystkie wątki zapisywały do znacznika śledzenia fd, a wątek o wysokim priorytecie mógł zostać zablokowany na znaczniku śledzenia fd, jeśli wątek o niskim priorytecie trzymał blokadę fd, a następnie został wywłaszczony. Gdy śledzenie zostało wyłączone dla wątków o niskim priorytecie, nie było problemu z wydajnością.
Nie byliśmy w stanie odtworzyć tego w żadnej innej sytuacji, ale warto to wskazać jako potencjalną przyczynę problemów z wydajnością podczas śledzenia.
Niepotrzebne przejścia w stanie bezczynności procesora
W przypadku IPC, zwłaszcza potoków wieloprocesowych, często można zaobserwować różnice w następującym zachowaniu środowiska wykonawczego:
- Wątek A działa na procesorze 1.
- Wątek A budzi wątek B.
- Wątek B zaczyna działać na procesorze 2.
- Wątek A natychmiast zasypia, aby zostać obudzony przez wątek B, gdy wątek B zakończy swoją bieżącą pracę.
Typowym źródłem narzutu jest etap pomiędzy krokami 2 i 3. Jeśli procesor 2 jest bezczynny, należy go przywrócić do stanu aktywnego, zanim wątek B będzie mógł działać. W zależności od SOC i głębokości biegu jałowego może upłynąć dziesiątki mikrosekund, zanim wątek B zacznie działać. Jeśli rzeczywisty czas działania każdej strony IPC jest wystarczająco zbliżony do narzutu, ogólna wydajność tego potoku może zostać znacznie zmniejszona przez przejścia bezczynności procesora. Najczęstszym miejscem, w którym Android może to osiągnąć, są transakcje segregatorów, a wiele usług korzystających z segregatorów wygląda podobnie do sytuacji opisanej powyżej.
Najpierw użyj funkcji wake_up_interruptible_sync()
w sterownikach jądra i obsługuj ją za pomocą dowolnego niestandardowego harmonogramu. Potraktuj to jako wymaganie, a nie wskazówkę. Binder używa tego dzisiaj i bardzo pomaga w synchronicznych transakcjach segregatorów, unikając niepotrzebnych przejść bezczynności procesora.
Po drugie, upewnij się, że czasy przejścia procesora są realistyczne i że zarządca procesora prawidłowo je uwzględnia. Jeśli Twój SOC wpada w najgłębszy stan bezczynności, nie zaoszczędzisz energii, przechodząc w najgłębszy stan bezczynności.
Logowanie
Rejestrowanie nie jest bezpłatne dla cykli procesora ani pamięci, więc nie spamuj bufora dziennika. Rejestrowanie cykli kosztów w Twojej aplikacji (bezpośrednio) i w demonie dziennika. Przed wysyłką urządzenia usuń wszelkie dzienniki debugowania.
Problemy z wejściami/wyjściami
Operacje we/wy są częstym źródłem zakłóceń. Jeśli wątek uzyskuje dostęp do pliku mapowanego w pamięci, a strony nie ma w pamięci podręcznej strony, powoduje błąd i odczytuje stronę z dysku. Blokuje to wątek (zwykle na ponad 10 ms), a jeśli zdarzy się to na ścieżce krytycznej renderowania interfejsu użytkownika, może spowodować jank. Istnieje zbyt wiele przyczyn operacji we/wy, aby je tutaj omówić, ale próbując poprawić zachowanie we/wy, sprawdź następujące lokalizacje:
- Usługa Pinnera . Dodana w systemie Android 7.0 usługa PinnerService umożliwia platformie blokowanie niektórych plików w pamięci podręcznej strony. Spowoduje to usunięcie pamięci do wykorzystania przez inny proces, ale jeśli istnieją pewne pliki, o których z góry wiadomo, że są regularnie używane, skuteczne może być zablokowanie tych plików.
Na urządzeniach Pixel i Nexus 6P z systemem Android 7.0 zmlockowaliśmy cztery pliki:- /system/framework/arm64/boot-framework.oat
- /system/framework/oat/arm64/services.odex
- /system/framework/arm64/boot.oat
- /system/framework/arm64/boot-core-libart.oat
- Szyfrowanie . Inna możliwa przyczyna problemów we/wy. Uważamy, że szyfrowanie inline zapewnia najlepszą wydajność w porównaniu z szyfrowaniem opartym na procesorze lub przy użyciu bloku sprzętowego dostępnego za pośrednictwem DMA. Co najważniejsze, szyfrowanie inline zmniejsza wahania związane z operacjami we/wy, szczególnie w porównaniu z szyfrowaniem opartym na procesorze. Ponieważ pobrania do pamięci podręcznej strony często odbywają się na ścieżce krytycznej renderowania interfejsu użytkownika, szyfrowanie oparte na procesorze powoduje dodatkowe obciążenie procesora na ścieżce krytycznej, co powoduje większe wahania niż samo pobieranie we/wy.
Sprzętowe silniki szyfrujące oparte na DMA mają podobny problem, ponieważ jądro musi spędzać cykle na zarządzaniu tą pracą, nawet jeśli dostępna jest inna krytyczna praca. Zdecydowanie zalecamy każdemu dostawcy SOC budującemu nowy sprzęt uwzględnienie obsługi szyfrowania wbudowanego.
Agresywne pakowanie małych zadań
Niektóre programy planujące oferują obsługę pakowania małych zadań na pojedyncze rdzenie procesora, aby spróbować zmniejszyć zużycie energii poprzez dłuższe utrzymywanie bezczynności większej liczby procesorów. Chociaż działa to dobrze w przypadku przepustowości i zużycia energii, może mieć katastrofalne skutki w przypadku opóźnień. Na ścieżce krytycznej renderowania interfejsu użytkownika znajduje się kilka krótkotrwałych wątków, które można uznać za małe; jeśli te wątki zostaną opóźnione w związku z powolną migracją do innych procesorów, spowoduje to jank. Zalecamy bardzo ostrożne pakowanie do małych zadań.
Błąd pamięci podręcznej strony
Urządzenie bez wystarczającej ilości wolnej pamięci może nagle stać się bardzo powolne podczas wykonywania długotrwałej operacji, takiej jak otwieranie nowej aplikacji. Ślad aplikacji może ujawnić, że jest ona konsekwentnie blokowana we/wy podczas określonego przebiegu, nawet jeśli często nie jest blokowana we/wy. Zwykle jest to oznaką niszczenia pamięci podręcznej stron, szczególnie na urządzeniach z mniejszą ilością pamięci.
Jednym ze sposobów zidentyfikowania tego jest pobranie pliku systrace przy użyciu znacznika pagecache i przesłanie tego śledzenia do skryptu pod adresem system/extras/pagecache/pagecache.py
. pagecache.py tłumaczy indywidualne żądania mapowania plików do pamięci podręcznej strony na zagregowane statystyki dotyczące poszczególnych plików. Jeśli okaże się, że odczytano więcej bajtów pliku niż całkowity rozmiar tego pliku na dysku, z pewnością natrafiasz na przeładowanie pamięci podręcznej strony.
Oznacza to, że zestaw roboczy wymagany przez obciążenie (zwykle pojedyncza aplikacja plus serwer_systemowy) jest większy niż ilość pamięci dostępnej dla pamięci podręcznej stron na Twoim urządzeniu. W rezultacie, gdy jedna część obciążenia pobiera potrzebne dane z pamięci podręcznej strony, inna część, która będzie używana w najbliższej przyszłości, zostanie wykluczona i będzie musiała zostać pobrana ponownie, co spowoduje ponowne wystąpienie problemu do czasu załadowania zakończył się. Jest to podstawowa przyczyna problemów z wydajnością, gdy na urządzeniu nie jest dostępna wystarczająca ilość pamięci.
Nie ma niezawodnego sposobu na naprawienie niszczenia pamięci podręcznej stron, ale istnieje kilka sposobów, aby spróbować ulepszyć ten problem na danym urządzeniu.
- Używaj mniej pamięci w procesach trwałych. Im mniej pamięci zużywają trwałe procesy, tym więcej pamięci jest dostępne dla aplikacji i pamięci podręcznej stron.
- Sprawdź wycięcia w urządzeniu, aby upewnić się, że nie usuwasz niepotrzebnie pamięci z systemu operacyjnego. Widzieliśmy sytuacje, w których wycinki używane do debugowania zostały przypadkowo pozostawione w dostarczanych konfiguracjach jądra, zużywając dziesiątki megabajtów pamięci. Może to stanowić różnicę między uderzeniem w pamięć podręczną strony, a nie, szczególnie na urządzeniach z mniejszą ilością pamięci.
- Jeśli widzisz, że pamięć podręczna strony ulega awarii na serwerze_systemowym w przypadku plików krytycznych, rozważ przypięcie tych plików. Zwiększy to wykorzystanie pamięci w innym miejscu, ale może zmodyfikować zachowanie na tyle, aby uniknąć bicia.
- Dostrój ponownie lowmemorykiller, aby spróbować zwolnić więcej pamięci. Progi lowmemorykiller opierają się zarówno na całkowicie wolnej pamięci, jak i na pamięci podręcznej strony, więc zwiększenie progu, przy którym procesy na danym poziomie oom_adj są zabijane, może skutkować lepszym zachowaniem kosztem zwiększonej śmierci aplikacji działającej w tle.
- Spróbuj użyć ZRAM-a. Na Pixelu używamy ZRAM-u, mimo że Pixel ma 4 GB, bo mogłoby pomóc w przypadku rzadko używanych brudnych stron.