메모리 리미터

Android 17 이상에는 Linux cgroup v2를 사용하여 애플리케이션 프로세스의 메모리 사용량을 모니터링하고 제한하는 시스템 서비스인 메모리 제한기가 포함되어 있습니다. 메모리 제한기는 개별 앱이 과도한 시스템 메모리를 소비하지 못하도록 방지하여 전반적인 메모리 압력을 줄이고 중요한 포그라운드 및 캐시된 프로세스의 공격적인 메모리 부족 종료를 방지합니다.

메커니즘

메모리 제한기는 활동 관리자 서비스 (AMS)와 통합되어 프로세스 수명 주기 이벤트와 상태 변경을 추적합니다. 메모리 제한기는 Linux 커널 cgroup v2 계층 구조를 사용하여 메모리 한도를 적용합니다.

메모리 제한기를 지원하려면 기기 커널에서 cgroup v2와 memory 컨트롤러를 사용 설정해야 합니다. 이 서비스는 특히 다음 속성에 의존합니다.

memory.high
소프트 한도입니다. 이 한도를 초과하면 프로세스가 제한되고 커널은 해당 프로세스에서 사전 페이지 회수를 시도합니다.
memory.swap.max
프로세스에서 사용할 수 있는 최대 스왑 공간 (예: ZRAM)을 제한합니다.

앱에 미치는 영향

메모리 한도 내에서 작동하는 앱은 메모리 제한기의 영향을 받지 않습니다.

앱이 memory.high 한도를 초과하면 커널은 앱의 클린 파일 지원 메모리를 삭제하고 비활성 익명 메모리를 스왑 아웃하여 앱을 한도 내로 유지합니다. 이 페이지 삭제 및 스왑 활동으로 인해 앱에 일시적인 실행 제한이 발생할 수 있지만 계속 실행됩니다.

스왑 공간이 소진된 후에도 앱이 계속 익명 메모리를 할당하는 제어되지 않는 메모리 누수와 같은 극단적인 상황에서는 앱이 메모리를 할당하지 못하고 종료됩니다.

프로세스 모니터링

메모리 제한기는 기본적으로 앱 프로세스 (UID >= 10000)를 모니터링합니다. 핵심 시스템 프로세스는 필수 시스템 안정성을 유지하기 위해 제외됩니다.

메모리 제한기는 프로세스의 공개 상태에 따라 메모리 한도를 할당합니다.

  • 표시되는 프로세스: 사용자에게 표시되는 UI (예: 현재 포커스가 맞춰진 활동 또는 활성 상호작용)를 호스팅하는 프로세스입니다. UI를 표시하려면 렌더링 및 그래픽 파이프라인에 더 큰 작업 세트가 필요하므로 표시되는 프로세스에는 더 많은 메모리 한도가 제공됩니다.

  • 표시되지 않는 프로세스: UI를 표시하지 않고 백그라운드에서 작업을 실행하는 프로세스 (예: 백그라운드 서비스 또는 브로드캐스트 수신기)입니다. UI 구성요소를 렌더링하지 않으므로 더 제한적인 한도에서 작동합니다.

다음 표에서는 특정 프로세스 상태를 메모리 한도 분류에 매핑합니다.

프로세스 상태메모리 한도
PERSISTENT제한 없음
PERSISTENT_UI제한 없음
TOP표시
BOUND_TOP표시
FOREGROUND_SERVICE표시 안함
BOUND_FOREGROUND_SERVICE표시 안함
IMPORTANT_FOREGROUND표시
IMPORTANT_BACKGROUND표시 안함
TRANSIENT_BACKGROUND표시 안함
BACKUP표시 안함
SERVICE표시 안함
RECEIVER표시 안함
TOP_SLEEPING표시
HEAVY_WEIGHT표시 안함
HOME표시 안함
LAST_ACTIVITY표시 안함
CACHED_ACTIVITY캐시됨
CACHED_ACTIVITY_CLIENT캐시됨
CACHED_RECENT캐시됨
CACHED_EMPTY캐시됨

캐시된 상태에서 프로세스는 고정된 후 최대한 회수됩니다.

프로세스가 할당된 memory.high 한도를 초과하면 메모리 제한기가 이벤트를 감지하고 메모리 프로필 캡처 또는 statsd에 비정상 로깅과 같은 진단 작업을 트리거할 수 있습니다.

구성

Android 17 이상에서 메모리 제한기는 시스템 파티션에 표준 플랫폼 기본 구성 파일을 제공하는 동시에 vendor 파티션에서 기기별 선택적 재정의를 지원합니다.

  • 시스템 기본 구성: /system/etc/memory-limiter-config.xml
  • 공급업체 재정의 (선택사항): /vendor/etc/memory-limiter-config.xml

메모리 제한기는 먼저 /vendor/etc/memory-limiter-config.xml을 확인합니다. 이 파일이 있으면 공급업체 구성을 사용하고, 그렇지 않으면 /system/etc/memory-limiter-config.xml의 기본 시스템 구성으로 대체됩니다.

XML 형식

구성 파일은 memory-limiter-config.xsd에 정의된 스키마를 따릅니다. 이 파일은 사용 가능한 메모리별로 정렬된 여러 한도 세트를 정의합니다. 서비스는 기기의 사용 가능한 RAM을 기반으로 가장 일치하는 한도 세트를 선택합니다. 모든 메모리 값은 메비바이트 (MiB)로 지정됩니다.

<MemoryLimiterConfig>
  <version>1</version>
  <configList>
    <!-- RAM minimums account for MemTotal excluding carve-outs. -->
    <limitSet>
      <!-- Limits for 16GB RAM device (MemTotal >= 13GiB): 10G/5G/5G/5G -->
      <minimumRequiredMemTotal>13312</minimumRequiredMemTotal>
      <memVisible>10240</memVisible>
      <memNotVisible>5120</memNotVisible>
      <swapVisible>5120</swapVisible>
      <swapNotVisible>5120</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 12GB RAM device (MemTotal >= 10GiB): 8G/4G/4G/4G -->
      <minimumRequiredMemTotal>10240</minimumRequiredMemTotal>
      <memVisible>8192</memVisible>
      <memNotVisible>4096</memNotVisible>
      <swapVisible>4096</swapVisible>
      <swapNotVisible>4096</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 8GB RAM device (MemTotal >= 6.5GiB): 5G/3G/3G/3G -->
      <minimumRequiredMemTotal>6656</minimumRequiredMemTotal>
      <memVisible>5120</memVisible>
      <memNotVisible>3072</memNotVisible>
      <swapVisible>3072</swapVisible>
      <swapNotVisible>3072</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 6GB RAM device (MemTotal >= 4.5GiB): 4G/2G/2G/2G -->
      <minimumRequiredMemTotal>4608</minimumRequiredMemTotal>
      <memVisible>4096</memVisible>
      <memNotVisible>2048</memNotVisible>
      <swapVisible>2048</swapVisible>
      <swapNotVisible>2048</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 4GB RAM device (MemTotal >= 3GiB): 2G/1G/1G/1G -->
      <minimumRequiredMemTotal>3072</minimumRequiredMemTotal>
      <memVisible>2048</memVisible>
      <memNotVisible>1024</memNotVisible>
      <swapVisible>1024</swapVisible>
      <swapNotVisible>1024</swapNotVisible>
    </limitSet>
  </configList>
</MemoryLimiterConfig>
version
구성 버전을 식별하는 양의 정수입니다. 이 값은 1이어야 합니다.
minimumRequiredMemTotal

이 한도 세트를 적용하는 데 필요한 최소 시스템 MemTotal (MiB)입니다. 이 서비스는 /proc/meminfo에서 커널이 보고한 총 사용 가능한 메모리에 대해 이 값을 평가합니다.

메모리 할당 이해: 커널 MemTotal 값은 하드웨어 및 펌웨어 메모리 할당이 차감된 후 운영체제에서 액세스할 수 있는 실제 DRAM을 반영합니다. 할당은 일반적으로 500MiB에서 1GiB 이상의 실제 메모리를 소비하는 GPU, 베이스밴드 모뎀, 카메라 ISP, 보안 실행 환경, 하이퍼바이저와 같은 특수 하드웨어 구성요소용 RAM을 예약합니다.

MemTotal은 기기의 광고된 실제 DRAM보다 낮으므로 minimumRequiredMemTotal 기준점에는 이러한 할당을 위한 여유 공간이 포함됩니다. 예를 들어 16GB 기기를 타겟팅하는 구성은 16384 (16GiB)가 아닌 13312(13GiB)의 minimumRequiredMemTotal을 지정하고 12GB 기기의 등급은 10240(10GiB)을 지정합니다. 이렇게 하면 보드 수준의 할당 변형과 관계없이 각 기기가 의도한 등급과 일치합니다.

memVisible

표시되는 프로세스에 적용되는 MiB의 소프트 메모리 한도 (memory.high)입니다.

memNotVisible

표시되지 않는 프로세스에 적용되는 MiB의 소프트 메모리 한도 (memory.high)입니다.

swapVisible

표시되는 프로세스에 적용되는 MiB의 스왑 공간 한도 (memory.swap.max)입니다.

swapNotVisible

표시되지 않는 프로세스에 적용되는 MiB의 스왑 공간 한도 (memory.swap.max)입니다.

표준 플랫폼 한도

Android 17 이상에서 플랫폼은 일반적인 실제 RAM 등급에서 /system/etc/memory-limiter-config.xml에 표준 메모리 및 스왑 한도를 지정합니다.

타겟 실제 RAM 최소 필요
MemTotal
(minimumRequiredMemTotal)
표시되는 메모리 한도
(memVisible)
표시되지 않는 메모리 한도
(memNotVisible)
표시되는 스왑 한도
(swapVisible)
표시되지 않는 스왑 한도
(swapNotVisible)
16GB 13,312MiB (13GiB) 10,240MiB (10GiB) 5,120MiB (5GiB) 5,120MiB (5GiB) 5,120MiB (5GiB)
12GB 10,240MiB (10GiB) 8,192MiB (8GiB) 4,096MiB (4GiB) 4,096MiB (4GiB) 4,096MiB (4GiB)
8GB 6,656MiB (6.5GiB) 5,120MiB (5GiB) 3,072MiB (3GiB) 3,072MiB (3GiB) 3,072MiB (3GiB)
6GB 4,608MiB (4.5GiB) 4,096MiB (4GiB) 2,048MiB (2GiB) 2,048MiB (2GiB) 2,048MiB (2GiB)
4GB 3,072MiB (3GiB) 2,048MiB (2GiB) 1,024MiB (1GiB) 1,024MiB (1GiB) 1,024MiB (1GiB)

메모리 한도 원칙

메모리 제한기 구성은 다음과 같은 플랫폼 원칙을 기반으로 설계되었습니다.

  • 생태계 일관성 및 앱 호환성: 기기 전반에서 일관된 메모리 한도를 사용하면 Android 생태계 전반에서 예측 가능한 앱 성능을 보장할 수 있습니다. 앱은 표준 메모리 예상치를 기준으로 개발되고 테스트됩니다. 균일한 플랫폼 한도를 유지하면 예기치 않은 제한 또는 조기 종료를 방지할 수 있습니다.

  • 비례 리소스 할당: 플랫폼 한도는 실제 RAM 용량에 따라 보정됩니다.

    • 표시되는 프로세스: 활성 UI, 합성, 렌더링 워크로드를 지원하기 위해 총 실제 RAM의 약 1/2~2/3이 할당됩니다.
    • 표시되지 않는 프로세스: 백그라운드 작업을 위해 총 실제 RAM의 약 1/4~1/3이 할당됩니다.
  • 범용 적용 가능성: 한도는 사전 설치된 시스템 및 OEM 앱을 포함하여 기기의 모든 애플리케이션 프로세스 (UID >= 10000)에 균일하게 적용됩니다. 메모리 제한기는 특정 앱을 제외하기 위한 허용 목록을 지원하지 않으므로 모든 앱에서 공정한 메모리 거버넌스를 보장합니다.

  • 런타임 쿼리 API 없음: Android 17 이상에서는 앱이 런타임에 할당된 메모리 한도를 프로그래매틱 방식으로 쿼리할 수 없습니다. 표준 플랫폼 한도는 정상적인 사용 중에 동작이 올바른 앱이 제약 없이 작동할 수 있도록 충분한 여유 공간으로 정의됩니다.

  • 커널 회수 및 스왑 동적: 앱 프로세스가 memory.high 한도에 접근하면 Linux 커널은 메모리 회수를 해당 프로세스 cgroup으로 구체적으로 안내합니다. 여기에는 비활성 파일 지원 페이지 삭제 및 콜드 익명 페이지를 ZRAM으로 스왑하여 전역 메모리 압력을 가하지 않고 시스템 유동성을 유지하는 것이 포함됩니다.

셸 명령어

am memory-limiter 명령어를 사용하면 개발자와 시스템 통합자가 개발, 테스트, 디버깅을 위해 런타임에 서비스와 상호작용할 수 있습니다.

am memory-limiter <SUB-COMMAND>

상태

status 하위 명령어는 메모리 제한기의 작동 상태와 활성 측정항목을 보고합니다.

adb shell am memory-limiter status

출력 예시:

Memory limiter
  enabled                  monitoring=true          ignored=none
  visibleMem=1948MB        visibleSwap=974MB
  notVisibleMem=974MB      notVisibleSwap=487MB
  started=36               watched=36               watch-failed=0
  events=0                 processes=36             process-hwm=36

출력의 주요 필드는 다음과 같습니다.

monitoring
메모리 제한기가 프로세스를 적극적으로 감시하는지 여부를 나타냅니다.
visibleMemnotVisibleMem
각 공개 상태에 현재 적용되는 계산된 절대 메모리 한도 (memory.high)입니다.
visibleSwapnotVisibleSwap
각 공개 상태에 현재 적용되는 계산된 절대 스왑 한도 (memory.swap.max)입니다.
events
프로세스가 할당된 한도를 초과한 횟수입니다.
processes
현재 모니터링되는 프로세스 수입니다.

ignore

ignore 하위 명령어는 특정 UID 또는 모든 프로세스를 메모리 제한에서 일시적으로 제외합니다. 이는 성능 벤치마킹, 스트레스 테스트 또는 메모리 동작 진단에 유용합니다.

# Ignore a specific UID
adb shell am memory-limiter ignore 10087
# Ignore all processes (temporarily disables limiting)
adb shell am memory-limiter ignore all
# Resume normal limiting operation
adb shell am memory-limiter ignore none

수동

manual 하위 명령어는 프로세스 ID (PID)별로 특정 프로세스의 계산된 한도를 바이트 단위의 맞춤 절대값으로 재정의합니다. 값은 정수여야 하지만 MiB를 나타내는 MB 서픽스 또는 GiB를 나타내는 GB 서픽스를 포함할 수 있습니다.

# Set a 1GiB limit for PID 1234
adb shell am memory-limiter manual 1234 1073741824
# Set a 1GiB limit for PID 1234
adb shell am memory-limiter manual 1234 1024MB
# Set a 1GiB limit for PID 1234
adb shell am memory-limiter manual 1234 1GB
# Remove the manual override for PID 1234
adb shell am memory-limiter manual 1234 none

수동 재정의는 특정 프로세스 인스턴스의 수명 동안만 적용됩니다. 프로세스가 다시 시작되면 상태에 따라 표준 한도로 돌아갑니다.

수동 재정의는 실제 시스템 한도를 초과할 수 없습니다.