프로세스 간 메모리 할당

Android 플랫폼은 사용 가능한 메모리가 있다는 것은 메모리 낭비라는 전제 하에 실행됩니다. 항상 사용 가능한 모든 메모리를 사용하려고 합니다. 예를 들어 시스템은 앱이 닫힌 후에도 앱을 메모리에 보관하여 사용자가 앱으로 빠르게 다시 전환할 수 있게 합니다. 이러한 이유로 Android 기기는 사용 가능한 메모리가 거의 없는 상태로 실행되는 경우가 많습니다. 메모리 관리는 중요한 시스템 프로세스와 여러 사용자 애플리케이션 사이에서 메모리를 올바르게 할당하는 데 매우 중요합니다.

이 페이지에서는 Android가 시스템 및 사용자 애플리케이션에 메모리를 할당하는 방법의 기본사항을 설명합니다. 운영체제가 메모리 부족 상황에 어떻게 반응하는지도 설명합니다.

메모리 유형

Android 기기에는 세 가지 메모리 유형 RAM, zRAM, 저장소가 있습니다. CPU와 GPU는 모두 동일한 RAM에 액세스합니다.

메모리 유형

그림 1. 메모리 유형 - RAM, zRAM, 저장소

  • RAM은 가장 빠른 메모리 유형이지만 일반적으로 크기가 제한됩니다. 고급형 기기에는 일반적으로 최대 크기의 RAM이 있습니다.

  • zRAM은 RAM의 파티션으로 스왑 공간에 사용됩니다. 모든 것은 zRAM에 배치될 때 압축되고 zRAM에서 복사될 때 압축이 해제됩니다. 이 부분의 RAM은 페이지가 zRAM으로 들어오거나 zRAM에서 나갈 때 크기가 커지거나 작아집니다. 기기 제조업체가 최대 크기를 설정할 수 있습니다.

  • 저장소에는 파일 시스템, 그리고 모든 앱 및 라이브러리, 플랫폼에 포함된 객체 코드와 같은 영구 데이터가 모두 포함됩니다. 저장소는 다른 두 메모리 유형보다 용량이 훨씬 큽니다. Android에서는 저장소가 다른 Linux 구현에서처럼 스왑 공간에 사용되지 않습니다. 빈번한 쓰기 작업으로 메모리에 마모가 발생하고 저장소 매체의 수명을 단축할 수 있기 때문입니다.

메모리 페이지

RAM은 페이지로 나뉩니다. 일반적으로 각 페이지는 4KB 메모리입니다.

페이지는 사용 가능하거나 사용된 것으로 간주됩니다. 사용 가능한 페이지는 사용되지 않은 RAM입니다. 사용된 페이지는 시스템에서 활발히 사용 중인 RAM이며 다음 카테고리로 그룹화됩니다.

  • 캐시됨: 저장소의 파일로 지원되는 메모리입니다(예: 코드 또는 메모리 매핑된 파일). 캐시된 메모리에는 두 가지 유형이 있습니다.
    • 비공개: 하나의 프로세스에서 소유하고 공유되지 않습니다.
      • 클린: 저장소에 있는 파일의 수정되지 않은 복사본. kswapd로 삭제하여 사용 가능한 메모리를 늘릴 수 있습니다.
      • 더티: 저장소에 있는 파일의 수정된 복사본. kswapd를 통해 zRAM으로 이동하거나 zRAM에서 압축하여 사용 가능한 메모리를 늘릴 수 있습니다.
    • 공유: 여러 프로세스에서 사용됩니다.
      • 클린: 저장소에 있는 파일의 수정되지 않은 복사본. kswapd로 삭제하여 사용 가능한 메모리를 늘릴 수 있습니다.
      • 더티: 저장소에 있는 파일의 수정된 복사본. kswapd를 통해 또는 msync()munmap()을 명시적으로 사용하여 저장소에 있는 파일에 변경사항을 다시 써서 사용 가능한 메모리를 늘릴 수 있습니다.
  • 익명: 저장소의 파일로 지원되지 않는 메모리입니다 (예: MAP_ANONYMOUS 플래그가 설정된 mmap()에 의해 할당됨).
    • 더티: kswapd를 통해 zRAM으로 이동하거나 zRAM에서 압축하여 사용 가능한 메모리를 늘릴 수 있습니다.

사용 가능한 페이지와 사용된 페이지의 비율은 시스템에서 활발하게 RAM을 관리하므로 시간이 지남에 따라 달라집니다. 이 섹션에서 소개된 개념이 메모리가 부족한 상황을 관리하는 데 핵심입니다. 이 문서의 다음 섹션에서는 이러한 개념을 더 상세히 설명합니다.

메모리 부족 관리

Android에는 메모리 부족 상황을 처리하는 두 가지 기본 메커니즘인 커널 스왑 데몬과 로우 메모리 킬러가 있습니다.

커널 스왑 데몬

커널 스왑 데몬(kswapd)은 Linux 커널의 일부이며 사용된 메모리를 사용 가능한 메모리로 변환합니다. 데몬은 기기의 사용 가능한 메모리가 부족해질 때 활성화됩니다. Linux 커널은 사용 가능한 메모리의 낮은 임계값과 높은 임계값을 유지합니다. 사용 가능한 메모리가 낮은 임계값 아래로 떨어지면 kswapd가 메모리를 회수하기 시작합니다. 사용 가능한 메모리가 높은 임계값에 도달하면 kswapd가 메모리 회수를 중지합니다.

kswapd는 수정되지 않았고 저장소로 지원되므로 클린 페이지를 삭제하여 회수할 수 있습니다. 프로세스가 삭제된 클린 페이지를 처리하려고 하면 시스템은 페이지를 저장소에서 RAM으로 복사합니다. 이 작업을 요구 페이징이라고 합니다.

저장소로 지원되는 삭제된 클린 페이지

그림 2. 저장소로 지원되는 삭제된 클린 페이지

kswapd는 캐시된 비공개 더티 페이지와 익명의 더티 페이지를 zRAM으로 이동할 수 있고 zRAM에서 이동한 페이지가 압축됩니다. 이렇게 하면 RAM에서 사용할 수 있는 메모리(사용 가능한 페이지)가 확보됩니다. 프로세스가 zRAM의 더티 페이지를 터치하려고 하면 페이지가 압축 해제되고 다시 RAM으로 이동합니다. 압축된 페이지와 연결된 프로세스가 종료되면 페이지가 zRAM에서 삭제됩니다.

사용 가능한 메모리 양이 특정 임계값 아래로 떨어지면 시스템에서는 프로세스를 종료하기 시작합니다.

zRAM으로 이동하여 압축된 더티 페이지

그림 3. zRAM으로 이동하여 압축된 더티 페이지

로우 메모리 킬러

kswapd로는 시스템에 충분한 메모리를 확보할 수 없는 경우가 많습니다. 이 경우 시스템은 onTrimMemory()를 사용하여 메모리가 부족하고 할당량을 줄여야 한다고 앱에 알립니다. 이 방법으로 충분하지 않으면 커널이 메모리를 확보하려고 프로세스를 종료하기 시작합니다. 이 작업을 실행하기 위해 로우 메모리 킬러(low-memory killer, LMK)를 사용합니다.

종료할 프로세스를 판단하기 위해 LMK는 oom_adj_score라는 '메모리 부족' 점수를 사용하여 실행 중인 프로세스의 우선순위를 정합니다. 최고 점수를 얻은 프로세스가 먼저 종료됩니다. 백그라운드 앱이 먼저, 시스템 프로세스가 마지막에 종료됩니다. 다음 표에는 LMK 점수 카테고리가 높은 점수에서 낮은 점수순으로 나열되어 있습니다. 최고 점수 카테고리인 첫 번째 행의 항목이 먼저 종료됩니다.

상단은 최고 점수, 하단은 최저 점수인 Android 프로세스

그림 4. 상단은 최고 점수, 하단은 최저 점수인 Android 프로세스

다음은 위 표의 다양한 카테고리를 설명합니다.

  • 백그라운드 앱: 이전에 실행되었고 현재는 활성화되지 않은 앱입니다. LMK는 oom_adj_score가 가장 높은 백그라운드 앱부터 먼저 종료합니다.

  • 이전 앱: 가장 최근에 사용한 백그라운드 앱입니다. 이전 앱은 백그라운드 앱보다 우선순위가 높고 점수가 낮습니다. 사용자가 백그라운드 앱보다 이전 앱으로 전환할 가능성이 높기 때문입니다.

  • 홈 앱: 런처 앱입니다. 이 앱을 종료하면 배경화면이 사라집니다.

  • 서비스: 서비스는 애플리케이션에서 시작되고 클라우드 동기화 또는 클라우드 업로드를 포함할 수 있습니다.

  • 인식할 수 있는 앱: 사용자가 어떤 식으로든 인식할 수 있는, 포그라운드에 없는 앱입니다(예: 작은 UI를 표시하는 검색 프로세스 실행 또는 음악 듣기).

  • 포그라운드 앱: 현재 사용 중인 앱입니다. 포그라운드 앱을 종료하면 애플리케이션이 비정상 종료되는 것처럼 보여서 사용자가 기기에 문제가 있다고 생각할 수 있습니다.

  • 영구(서비스): 텔레포니, Wi-Fi와 같은 기기의 핵심 서비스입니다.

  • 시스템: 시스템 프로세스입니다. 이러한 프로세스가 종료되면 휴대전화가 재부팅된 것처럼 보일 수 있습니다.

  • 네이티브: 시스템에서 사용하는 매우 낮은 수준의 프로세스입니다(예: kswapd).

기기 제조업체는 LMK의 동작을 변경할 수 있습니다.

메모리 공간 계산

커널은 시스템의 메모리 페이지를 모두 추적합니다.

다양한 프로세스에서 사용하는 페이지

그림 5. 다양한 프로세스에서 사용하는 페이지

앱에서 사용 중인 메모리 양을 파악할 때 시스템은 공유 페이지를 고려해야 합니다. 동일한 서비스 또는 라이브러리에 액세스하는 앱은 메모리 페이지를 공유합니다. 예를 들어 Google Play 서비스와 게임 앱이 위치 서비스를 공유할 수 있습니다. 이로 인해 각 애플리케이션에 비해 위치 서비스에 속하는 전체 메모리 양을 파악하기가 어렵습니다.

두 앱에서 공유한 페이지

그림 6. 두 앱에서 공유한 페이지(가운데)

애플리케이션의 메모리 공간을 파악하려면 다음 측정항목 중 하나를 사용하면 됩니다.

  • Resident Set Size(RSS): 앱에서 사용하는 공유 및 비공유 페이지 수입니다.
  • Proportional Set Size(PSS): 앱에서 사용하는 비공유 페이지 수 및 공유 페이지의 균등한 배포입니다(예: 프로세스 3개가 3MB를 공유하는 경우 각 프로세스는 PSS 1MB를 얻음).
  • Unique Set Size(USS): 앱에서 사용하는 비공유 페이지 수입니다. 공유 페이지는 포함되지 않습니다.

PSS는 페이지가 여러 번 계산되지 않으므로 모든 프로세스에서 사용하는 메모리 양을 파악하려는 운영체제에 유용합니다. PSS는 계산하는 데 시간이 오래 걸립니다. 시스템에서 어떤 페이지가 공유되는지, 얼마나 많은 프로세스에서 공유하는지 확인해야 하기 때문입니다. RSS는 공유 페이지와 비공유 페이지를 구별하지 않아서 계산 시간이 더 빠르고 메모리 할당의 변경사항을 추적하는 데 더 좋습니다.

추가 리소스

  • 참고: JavaScript가 사용 중지되어 있으면 링크 텍스트가 표시됩니다.
  • 앱 시작 시간