잡음은 인지 가능한 작업이 실행되지 못하게 막는 임의의 시스템 동작입니다. 이 페이지에서는 잡음 관련 버벅거림 문제를 식별하고 해결하는 방법을 설명합니다.
애플리케이션 스레드 스케줄러 지연
스케줄러 지연은 잡음의 가장 명백한 증상으로, 잡음은 실행되어야 하는 프로세스가 실행 가능하지만 긴 시간 동안 실행되지 않는 것을 의미합니다. 지연의 심각성은 상황에 따라 다릅니다. 예:
- 앱 내 임의의 도우미 스레드가 문제없이 수 밀리초 동안 지연될 수 있습니다.
- 애플리케이션의 UI 스레드는 1~2ms의 잡음을 허용할 수 있습니다.
- SCHED_FIFO로 실행 중인 드라이버 kthread는 실행 전에 500us 동안 실행 가능 상태인 경우 문제를 일으킬 수 있습니다.
실행 가능 시간은 systrace에서 스레드의 실행 중인 세그먼트 앞에 있는 파란색 막대로 식별할 수 있습니다. 또한 실행 가능 시간은 스레드의 sched_wakeup
이벤트와 스레드 실행 시작을 알리는 sched_switch
이벤트 간의 기간으로 결정될 수 있습니다.
너무 오래 실행되는 스레드
너무 오래 실행 가능 상태인 애플리케이션 UI 스레드는 문제를 일으킬 수 있습니다. 하위 스레드의 실행 가능 시간이 길어지는 원인에는 일반적으로 여러 가지가 있는데, UI 스레드의 실행 가능 시간을 0으로 만들기 위해서는 하위 수준 스레드의 실행 가능 시간을 늘리는 동일한 문제 몇 가지를 해결해야 할 수 있습니다. 지연을 줄이는 방법:
- 열 제한에 설명된 대로 cpuset를 사용합니다.
- CONFIG_HZ 값을 늘립니다.
- 이전에 이 값은 arm 및 arm64 플랫폼에서 100으로 설정되었습니다. 하지만 이 100이라는 값은 우연히 그렇게 설정된 것일 뿐 대화형 기기에 사용하기에 적합한 값은 아닙니다. CONFIG_HZ=100은 한 jiffy 단위가 10ms임을 의미합니다. 즉, CPU 간의 부하 분산이 발생하는 데 20ms(2jiffy)가 걸릴 수 있습니다. 이 기간은 로드된 시스템에서 발생하는 버벅거림에 상당한 영향을 미칠 수 있습니다.
- 최신 기기(Nexus 5X, Nexus 6P, Pixel, Pixel XL)는 CONFIG_HZ=300으로 설정된 상태로 제공됩니다. 이 설정값은 실행 가능 시간을 대폭 개선하지만 발생하는 전력 비용은 미미합니다. CONFIG_HZ를 변경한 후 전력 소비 또는 성능 문제가 크게 증가하면 드라이버 중 하나가 밀리초가 아닌 원시 jiffy 기준 타이머를 사용하는 경우 및 jiffy로 변환하는 경우가 원인일 수 있습니다. 이 문제는 일반적으로 쉽게 해결할 수 있습니다(CONFIG_HZ=300으로 변환 시 Nexus 5X 및 6P에서 kgsl 타이머 문제를 해결하는 패치 참고).
- 마지막으로, Nexus/Pixel에서 CONFIG_HZ=1000으로 실험하여 이 설정값이 RCU 오버헤드 감소로 인해 성능 및 전력 소비를 눈에 띄게 줄인다는 사실을 알았습니다.
이러한 두 가지 변경사항만으로도 부하 발생 시 기기에서 UI 스레드 실행 가능 시간이 훨씬 향상됩니다.
sys.use_fifo_ui 사용
sys.use_fifo_ui
속성을 1로 설정하여 UI 스레드 실행 가능 시간을 0으로 설정해 볼 수 있습니다.
경고: 용량을 고려하는 RT 스케줄러가 없는 경우에는 이기종 CPU 구성에서 이 옵션을 사용하지 마세요.
현재 공급되는 RT 스케줄러 중에는 용량을 고려하는 스케줄러가 없습니다. EAS용으로 용량을 고려하는 RT 스케줄러를 개발 중이지만 아직 사용할 수 없습니다. 기본 RT 스케줄러는 RT 우선순위, 그리고 CPU에 우선순위가 같거나 더 높은 RT 스레드가 있는지 여부만을 기준으로 합니다.
따라서 기본 RT 스케줄러는 우선순위가 높은 FIFO kthread가 동일한 빅 코어에서 절전 모드에서 해제되는 경우 상대적으로 오래 실행 중인 UI 스레드를 고주파수 빅 코어에서 최저 주파수의 리틀 코어로 이동합니다. 그러면 상당한 성능 회귀가 발생합니다. 이 옵션은 제공되는 Android 기기에서 아직 사용되지 않았으므로 이 옵션을 사용하려는 경우에는 Android 성능팀에 문의하여 검증을 받으세요.
sys.use_fifo_ui
를 사용 설정하면 ActivityManager가 최상위 애플리케이션의 UI 스레드와 RenderThread(UI에 가장 중요한 스레드 2개)를 추적해 스레드를 SCHED_OTHER 대신 SCHED_FIFO로 만듭니다. 이렇게 하면 UI 스레드 및 RenderThread에서 잡음을 효과적으로 제거할 수 있습니다. 이 옵션을 사용 설정한 상태에서 수집한 트레이스는 실행 가능 시간을 밀리초 대신 마이크로초 단위로 표시합니다.
하지만 RT 부하 분산기가 용량을 고려하지 않고, 애플리케이션 앱 시작을 담당하는 UI 스레드가 2.1Ghz 골드 Kryo 코어에서 1.5GHz 실버 Kryo 코어로 이전되었기 때문에 애플리케이션 시작 성능이 30% 감소했습니다. 용량을 고려하는 RT 부하 분산기를 사용하면 대량 작업에서 동일한 성능을 발휘하고, 여러 UI 업계 기준치에서는 95번째~99번째 백분위수 프레임 시간이 10~15% 감소할 것으로 기대됩니다.
인터럽트 트래픽
ARM 플랫폼은 기본적으로 CPU 0에만 인터럽트를 전달하므로 IRQ 밸런서(Qualcomm 플랫폼의 irqbalance 또는 msm_irqbalance)를 사용하는 것이 좋습니다.
Pixel 개발 중에는 인터럽트가 너무 많은 CPU 0이 직접적인 원인일 수 있는 버벅거림 현상을 관찰했습니다. 예를 들어 mdss_fb0
스레드가 CPU 0에서 예정된 경우 scanout 직전 디스플레이에서 트리거하는 인터럽트 때문에 버벅거림이 발생할 가능성이 훨씬 컸습니다. mdss_fb0
은 기한이 매우 빠듯한 작업을 진행 중인데 MDSS 인터럽트 핸들러에 약간의 시간이 걸릴 수 있습니다. 처음에 인터럽트와의 경합을 피하기 위해 mdss_fb0 스레드의 CPU 선호도를 CPU 1~3으로 설정하려고 했으나 msm_irqbalance를 아직 사용 설정하지 않았음을 알았습니다. msm_irqbalance를 사용 설정하면 다른 인터럽트의 경합이 줄어 mdss_fb0 및 MDSS 인터럽트가 둘 다 동일한 CPU에 있더라도 버벅거림이 눈에 띄게 개선되었습니다.
이는 systrace에서 irq 섹션 및 sched 섹션을 살펴보면 확인할 수 있습니다. sched 섹션에는 예약된 항목이 표시되지만, irq 섹션의 중복 영역은 이 기간 중 정상적으로 예약된 프로세스 대신 인터럽트가 실행됨을 의미합니다. 인터럽트 중에 상당한 시간이 소요되는 경우 옵션은 다음과 같습니다.
- 인터럽트 핸들러를 더 빠르게 만듭니다.
- 애초에 인터럽트 발생을 방지합니다.
- 인터럽트가 방해할 수 있는 다른 정규 작업이 있는 단계를 벗어나도록 인터럽트 실행 빈도를 변경합니다(일반 인터럽트인 경우).
- 인터럽트의 CPU 선호도를 직접 설정하고 CPU 사용 균형을 맞출 수 없게 합니다.
- 인터럽트를 피하기 위해 인터럽트가 방해하는 스레드의 CPU 선호도를 설정합니다.
- 인터럽트 밸런서를 사용하여 부하가 적은 CPU로 인터럽트를 이동합니다.
CPU 선호도 설정은 일반적으로 권장되지 않지만 특정한 경우에 유용할 수 있습니다. 일반적으로 대부분의 일반 인터럽트에 관한 시스템 상태를 예측하기는 매우 어렵지만 시스템이 정상적인 경우보다 더 많이 제한된 상태에서 특정 인터럽트를 트리거하는 매우 구체적인 조건이 있는 경우(예: VR) 분명한 CPU 선호도는 좋은 해결 방법이 될 수 있습니다.
긴 softirq
softirq는 실행되는 동안 선점을 사용 중지합니다. 또한 커널 내의 여러 위치에서 트리거될 수 있으며 사용자 프로세스 내에서 실행할 수 있습니다. softirq 활동이 충분하면 사용자 프로세스가 softirq 실행을 중지하고 ksoftirqd가 절전 모드에서 해제되어 softirq를 실행하고 부하 분산을 실행합니다. 일반적으로 이와 같은 작동은 문제가 없습니다. 하지만 단일 softirq가 매우 오래 실행되는 경우 시스템에 큰 문제가 발생할 수 있습니다.
softirq는 트레이스의 irq 섹션에서 확인할 수 있으므로 추적하는 동안 문제를 재현할 수 있는 경우 쉽게 찾을 수 있습니다. softirq는 사용자 프로세스 내에서 실행될 수 있기 때문에 잘못된 softirq는 명확한 이유 없이 사용자 프로세스 내부에서 추가 런타임으로 표시될 수 있습니다. 이 문제가 발생하면 irq 섹션에서 softirq가 원인인지 확인하세요.
드라이버가 선점 또는 IRQ를 사용 중지된 상태로 너무 오래 둠
선점 또는 인터럽트를 너무 길게(수십 밀리초) 사용 중지하면 버벅거림이 발생합니다. 일반적으로 버벅거림은 실행 가능한 스레드가 다른 스레드보다 우선순위(또는 SCHED_FIFO)가 크게 높은데도 불구하고 특정 CPU에서 실행되지 않는 상황으로 나타납니다.
몇 가지 가이드라인은 다음과 같습니다.
- 실행 가능한 스레드가 SCHED_FIFO이고 실행 중인 스레드가 SCHED_OTHER이면 실행 중인 스레드에 선점 또는 인터럽트가 사용 중지된 것입니다.
- 실행 가능한 스레드의 우선순위(100)가 실행 중인 스레드의 우선순위(120)보다 높은데, 실행 가능한 스레드가 2jiffy 이내에 실행되지 않는 경우, 실행 중인 스레드에 선점 또는 인터럽트가 사용 중지되었을 수 있습니다.
- 실행 가능 스레드와 실행 중인 스레드의 우선순위가 같은데 실행 가능한 스레드가 20ms 이내에 실행되지 않는 경우, 실행 중인 스레드에 선점 또는 인터럽트가 사용 중지되었을 수 있습니다.
인터럽트 핸들러를 실행하면 다른 인터럽트를 처리할 수 없으므로 선점도 중지됩니다.
문제의 영역을 식별하는 또 다른 옵션은 preemptirqsoff 추적기를 사용하는 것입니다(동적 ftrace 사용 참고). 이 추적기는 중단할 수 없는 영역(예: 함수 이름)의 근본 원인을 훨씬 더 상세하게 파악할 수 있지만 추적기를 사용하려면 추가 작업이 필요합니다. 추적기는 성능에 상당한 영향을 미칠 수 있지만 사용해볼 가치가 있습니다.
작업 대기열의 잘못된 사용
인터럽트 핸들러는 인터럽트 컨텍스트 외부에서 실행할 수 있는 작업을 하여 커널의 다른 스레드에 작업을 배분해야 할 때가 있습니다. 드라이버 개발자가 커널에 작업 대기열이라는 매우 편리한 시스템 차원의 비동기 작업 기능이 있어 커널에서 인터럽트 관련 작업에 사용할 수 있다는 사실을 알아챌 수 있습니다.
하지만 작업 대기열은 언제나 SCHED_OTHER이기 때문에 작업 대기열을 사용해 이 문제가 해결되는 경우는 거의 없습니다. 다수의 하드웨어 인터럽트는 성능의 중요한 경로에 있기 때문에 즉시 실행해야 합니다. 작업 대기열은 실행 시점을 보장하지 않습니다. 성능의 중요한 경로에서 작업 대기열을 확인했을 때마다 기기에 상관없이 작업 대기열이 산발적 버벅거림의 원인이었습니다. Pixel에서 플래그십 프로세서를 사용하는 경우 기기 부하 시 스케줄러 동작 및 시스템에서 실행 중인 다른 항목에 따라 단일 작업 대기열이 최대 7ms까지 지연될 수 있음을 관찰했습니다.
작업 대기열 대신 별도의 스레드 내에서 인터럽트 유사 작업을 처리해야 하는 드라이버는 자체 SCHED_FIFO kthread를 만들어야 합니다. kthread_work 함수를 사용하여 과정을 실행하는 데 도움을 받으려면 이 패치를 참고하세요.
프레임워크 잠금 경합
프레임워크 잠금 경합은 버벅거림 또는 다른 성능 문제의 원인이 될 수 있습니다. 일반적으로 원인은 ActivityManagerService 잠금이지만, 다른 잠금에서도 발생할 수 있습니다. 예를 들어 PowerManagerService 잠금은 화면 표시 성능에 영향을 줄 수 있습니다. 이 문제가 기기에서 발생하는 경우 프레임워크의 아키텍처 개선을 통해서만 문제가 좋아질 수 있기 때문에 좋은 해결 방법은 없습니다. 그러나 system_server 내에서 실행되는 코드를 수정하는 경우 잠금을 장시간 유지하지 않는 것이 중요하고, 특히 ActivityManagerService 잠금의 경우 더 그렇습니다.
바인더 잠금 경합
이전에 바인더에는 전역 잠금이 하나만 있었습니다. 잠금을 유지하는 동안 바인더 트랜잭션을 실행하는 스레드를 선점한 경우 원래 스레드가 잠금을 해제할 때까지 다른 스레드가 바인더 트랜잭션을 실행할 수 없었습니다. 이러한 동작은 적절하지 않은데, 바인더 경합이 UI 업데이트를 디스플레이로 전송하는 것을 포함하여 시스템의 모든 작업을 차단할 수 있기 때문입니다(UI 스레드는 바인더를 통해 SurfaceFlinger와 통신).
Android 6.0에는 바인더 잠금을 유지하면서 선점을 사용 중지하여 이 동작을 개선할 수 있는 여러 패치가 포함되었습니다. 바인더 잠금은 실제 런타임의 몇 마이크로초 동안 유지되어야 하기 때문에 이러한 조치는 안전합니다. 이 조치는 경합하지 않는 상태에서 성능을 크게 개선했고, 바인더 잠금을 유지하는 동안 대부분의 스케줄러 스위치 작동을 방지하여 경합을 방지했습니다. 하지만 바인더 잠금을 유지하는 전체 런타임 동안 선점을 사용 중지할 수는 없었습니다. 즉, copy_from_user와 같이 절전 모드로 전환할 수 있는 함수에는 선점이 사용 설정되었으며, 이로 인해 해결하고자 했던 처음 사례와 동일한 선점이 발생할 수 있었습니다. 패치를 업스트림에 전송하자마자 이는 최악의 아이디어라는 말을 들었습니다. (이에 동의했고 버벅거림을 방지하기 위한 패치의 효과에 관해서도 할 말이 없었습니다.)
프로세스 내에서 fd 경합
이는 드문 경우입니다. 이 문제가 버벅거림의 원인인 경우는 아마 없을 것입니다.
즉, 프로세스 내에 동일한 fd를 쓰는 스레드가 여러 개 있는 경우 이 fd에 관한 경합을 관찰할 수 있습니다. 하지만 Pixel bringup을 실행하는 동안 이러한 경합이 관찰된 유일한 경우는 우선순위가 높은 단일 스레드가 동일한 프로세스 내에서 실행 중이었을 때 우선순위가 낮은 스레드가 모든 CPU 시간을 차지하려고 시도했던 테스트 중이었습니다. 모든 스레드가 트레이스 마커 fd에 쓰고 있었고 우선순위가 낮은 스레드가 fd 잠금을 유지한 다음 선점되었을 경우 트레이스 마커 fd에서 우선순위가 높은 스레드가 차단될 수 있었습니다. 우선순위가 낮은 스레드에서 트레이스가 사용 중지된 경우 성능 문제가 발생하지 않았습니다.
이 문제는 다른 상황에서는 재현할 수 없었지만 추적하는 동안 성능 문제의 원인으로 지적할 만했습니다.
불필요한 CPU의 비활성 상태 전환
IPC, 특히 다중 프로세스 파이프라인을 처리할 때 다음과 같은 런타임 동작의 변형을 일반적으로 관찰할 수 있습니다.
- 스레드 A가 CPU 1에서 실행됩니다.
- 스레드 A가 스레드 B를 절전 모드에서 해제합니다.
- 스레드 B가 CPU 2에서 실행을 시작합니다.
- 스레드 A는 바로 절전 모드로 전환되고, 스레드 B가 현재 작업을 마치면 스레드 A를 절전 모드에서 해제합니다.
오버헤드의 일반적인 원인은 2단계와 3단계 사이에 있습니다. CPU 2가 비활성 상태이면 스레드 B를 실행하기 전에 활성 상태로 되돌려야 합니다. SOC 및 비활성 상태의 정도에 따라 스레드 B가 실행되기 전에 수십 마이크로초가 걸릴 수 있습니다. IPC의 각 측면의 실제 런타임이 오버헤드에 충분히 가까우면 CPU의 비활성 상태 전환에 의해 파이프라인의 전반적인 성능이 크게 저하될 수 있습니다. Android에서 이러한 상황이 가장 일반적으로 발생하는 위치는 바인더 트랜잭션 근처이고, 바인더를 사용하는 다수의 서비스에서 위에 설명한 상황이 나타날 수 있습니다.
첫째, 커널 드라이버에서 wake_up_interruptible_sync()
함수를 사용하고 이를 모든 맞춤 스케줄러에서 지원합니다. 이 함수를 힌트가 아닌 요구사항으로 처리합니다. 오늘날 바인더는 이 함수를 사용하는데 이는 동기식 바인더 트랜잭션에 크게 도움이 되어 CPU의 불필요한 비활성 상태 전환을 방지합니다.
둘째, cpuidle 전환 시간이 현실적이고 cpuidle 거버너가 이 시간을 제대로 고려하고 있는지 확인합니다. 가장 심한 비활성 상태 안팎에서 SOC에 스래싱이 발생하면 가장 심한 비활성 상태로 전환하여 전력을 절약할 수 없습니다.
로깅
로깅에는 CPU 주기 또는 메모리 관련 사항을 기록할 여유가 없으므로 로그 버퍼를 함부로 사용하지 마세요. 로깅은 애플리케이션 주기와 로그 데몬에 부정적인 영향을 미치는데, 애플리케이션 주기는 직접적인 영향을 받습니다. 기기를 배송하기 전에 모든 디버깅 로그를 삭제하세요.
I/O 문제
I/O 작업은 잡음의 일반적인 원인입니다. 스레드가 메모리 매핑 파일에 액세스하고 페이지가 페이지 캐시에 없으면 스레드에서 오류가 발생하고 스레드는 디스크에서 페이지를 읽습니다. 이 경우 스레드가 차단되는데(일반적으로 10ms 이상) UI 렌더링의 중요한 경로에서 이 문제가 발생하면 버벅거림이 발생할 수 있습니다. 여기서 다룰 I/O 작업의 원인이 너무 많지만 I/O 동작을 개선하려고 하면 다음 위치를 확인하세요.
- PinnerService. Android 7.0에 추가된 PinnerService를 사용하면 프레임워크에서 페이지 캐시의 일부 파일을 잠글 수 있습니다. 그러면 다른 프로세스에서 사용할 수 있도록 메모리를 삭제하지만 정기적으로 사용되는 것으로 알려진 파일이 몇 개 있는 경우 이러한 파일의 메모리를 잠그는 것이 효과적일 수 있습니다.
Android 7.0을 실행하는 Pixel 및 Nexus 6P 기기에서 다음 4개 파일의 메모리를 잠갔습니다.- /system/framework/arm64/boot-framework.oat
- /system/framework/oat/arm64/services.odex
- /system/framework/arm64/boot.oat
- /system/framework/arm64/boot-core-libart.oat
- 암호화. I/O 문제의 가능한 또 다른 원인입니다. 인라인 암호화는 CPU 기반 암호화와 비교하는 경우 또는 DMA를 통해 액세스 가능한 하드웨어 블록을 사용하는 경우 최상의 성능을 제공합니다. 무엇보다 인라인 암호화는 특히 CPU 기반 암호화와 비교했을 때 I/O와 관련된 잡음을 줄입니다. 페이지 캐시로 가져오기는 일반적으로 UI 렌더링의 중요한 경로에 있기 때문에 CPU 기반 암호화는 중요한 경로에서 추가 CPU 부하를 일으키는데, 이로 인해 단순한 I/O 가져오기보다 더 많은 잡음이 추가됩니다.
다른 중요한 작업을 실행할 수 있는 경우에도 커널이 이러한 작업을 관리하는 사이클을 소비해야 하기 때문에 DMA 기반 하드웨어 암호화 엔진에서도 유사한 문제가 발생합니다. Google에서는 모든 SOC 공급업체가 인라인 암호화를 지원하는 새 하드웨어를 제작할 것을 적극 권장합니다.
적극적인 소규모 작업 패킹
일부 스케줄러는 더 많은 CPU를 비활성 상태로 더 오래 유지하여 전력 소비를 줄이기 위해 단일 CPU 코어에 소규모 작업을 패킹하는 지원을 제공합니다. 이러한 지원은 처리량 및 전력 소비에는 효과가 있지만 지연 시간에는 매우 심각한 문제가 될 수 있습니다. UI 렌더링의 중요한 경로에는 소규모로 간주할 수 있는 짧게 실행되는 여러 개의 스레드가 있습니다. 이러한 스레드가 다른 CPU로 천천히 이전될 때 지연이 발생하면 버벅거림이 발생합니다. 소규모 작업 패킹은 신중하게 사용하는 것이 좋습니다.
페이지 캐시 스래싱
새 애플리케이션을 여는 등 장기 실행 작업을 하는 동안 여유 메모리가 부족한 기기가 갑자기 매우 느려질 수 있습니다. 애플리케이션의 트레이스는 I/O에서 차단되지 않더라도 특정 실행 중에 I/O에서 일관되게 차단되는 것으로 나타날 수 있습니다. 이는 일반적으로 페이지 캐시 스래싱의 징후로, 특히 메모리가 적은 기기에서 나타납니다.
이 문제를 파악하는 한 가지 방법은 pagecache 태그를 사용하여 systrace를 가져와 system/extras/pagecache/pagecache.py
의 스크립트에 트레이스를 제공하는 것입니다. pagecache.py는 페이지 캐시로 파일을 매핑하는 개별 요청을 파일별 집계 통계로 변환합니다. 디스크에 있는 파일의 총 크기보다 더 많은 바이트의 파일을 읽은 경우 페이지 캐시 스래싱이 발생합니다.
즉, 워크로드에 필요한 작업 세트(일반적으로 단일 애플리케이션과 system_server)가 기기의 페이지 캐시에 사용할 수 있는 메모리 양보다 많습니다. 따라서 워크로드의 한 부분이 페이지 캐시에서 필요한 데이터를 가져오고, 가까운 미래에 사용될 다른 부분이 제거되는데 다시 가져와야 하므로 로드가 완료될 때까지 문제가 다시 발생합니다. 이는 기기에서 사용 가능한 메모리가 충분하지 않을 때 성능 문제의 근본 원인입니다.
페이지 캐시 스래싱을 해결할 수 있는 확실한 방법은 없지만 특정 기기에서 이를 개선할 수 있는 몇 가지 방법이 있습니다.
- 영구 프로세스에서 메모리를 적게 사용합니다. 영구 프로세스에서 사용하는 메모리가 적을수록 애플리케이션과 페이지 캐시에 더 많은 메모리를 사용할 수 있습니다.
- OS에서 메모리를 불필요하게 제거하지 않도록 기기용으로 있는 carveout을 감사합니다. 디버깅에 사용된 carveout이 배송 커널 구성에 실수로 남아 있어 수십 MB의 메모리를 소모하는 상황이 관찰되었습니다. 특히 메모리가 적은 기기에서는 페이지 캐시 스래싱 발생과 그렇지 않은 경우 간에 차이가 발생할 수 있습니다.
- 중요한 파일의 system_server에서 페이지 캐시 스래싱이 발생하면 파일을 고정해보세요. 그러면 다른 곳에서 메모리 압력이 증가하지만 스래싱을 방지하기에 충분하도록 동작을 변경할 수 있습니다.
- 추가 메모리 확보를 위해 lowmemorykiller를 다시 조정합니다. lowmemorykiller의 기준점은 절대 여유 메모리와 페이지 캐시를 기반으로 하므로 지정된 oom_adj 수준의 프로세스가 종료되는 기준점을 늘리면 종료되는 백그라운드 앱이 늘어나 동작이 개선될 수 있습니다.
- ZRAM을 사용해보세요. Pixel에는 4GB의 RAM이 있지만, Pixel은 거의 사용되지 않는 더티 페이지에 유용할 수 있는 ZRAM을 사용하고 있습니다.