Google은 흑인 공동체를 위한 인종 간 평등을 진전시키기 위해 노력하고 있습니다. Google에서 어떤 노력을 하고 있는지 확인하세요.

RAM 부족 구성

Android는 RAM이 512MB인 기기를 지원합니다. 이 문서는 OEM이 메모리 용량이 적은 기기에서 Android 커널 4.4를 최적화 및 구성하도록 돕기 위해 작성되었습니다. 이러한 최적화 중 일부는 일반적인 내용이라 이전 버전에도 적용할 수 있습니다.

Android 커널 4.4 플랫폼 최적화

향상된 메모리 관리

  • 검증된 메모리 절약 커널 구성: zram으로 스왑합니다.
  • 캐시된 프로세스가 캐시 취소되려고 하거나 너무 크면 종료합니다.
  • 대규모 서비스가 A 서비스로 다시 분류되지 않도록 합니다. 그렇지 않으면 이러한 서비스로 인해 런처가 종료될 수 있습니다.
  • 유휴 상태 유지보수 시 너무 커진 프로세스를 종료합니다. 현재 IME와 같이 일반적으로 종료할 수 없는 프로세스도 종료합니다.
  • 백그라운드 서비스 시작을 직렬화합니다.
  • RAM이 부족한 기기의 메모리 사용 조정: OOM(메모리 부족) 조정 수준을 더욱 엄격하게 조정하고 그래픽 캐시를 더 줄입니다.

시스템 메모리 감소

  • system_server 및 시스템 UI 프로세스를 정리했습니다(MB 다소 절약).
  • Dalvik에서 dex 캐시를 미리 로드합니다(MB 다소 절약).
  • JIT-off 옵션을 확인했습니다(프로세스당 최대 1.5MB 절약).
  • 프로세스당 글꼴 캐시 오버헤드를 줄였습니다.
  • ArrayMap/ArraySet을 도입하여 프레임워크에서 HashMap/HashSet의 저장 공간이 더 적은 대체항목으로 사용합니다.

Procstats

실행 빈도 및 메모리 사용량에 따라 메모리 상태 및 앱 메모리 사용량을 표시하는 개발자 옵션을 추가했습니다.

API

앱이 메모리가 부족한 기기에서 실행되는 경우를 감지하고, RAM을 많이 사용하는 기능을 사용 중지할 수 있도록 ActivityManager.isLowRamDevice()를 추가했습니다.

메모리 추적

그래픽 메모리 할당, dumpsys meminfo의 추가 정보, meminfo의 명확한 요약을 추적하기 위해 memtrack HAL을 추가했습니다. 예를 들어 보고된 여유 RAM에는 캐시된 프로세스의 RAM이 포함되어 있기 때문에 OEM이 잘못된 최적화를 시도하지 않습니다.

빌드 시간 구성

RAM 부족 기기 플래그

ActivityManager.isLowRamDevice() 플래그는 메모리가 부족한 기기에서 제대로 작동하지 않는, 메모리 사용량이 많은 특정 기능을 사용 중지할지 결정합니다.

512MB 기기의 경우 이 플래그는 true를 반환합니다. 이 플래그는 기기 Makefile에서 다음 시스템 속성으로 사용 설정할 수 있습니다.

    PRODUCT_PROPERTY_OVERRIDES += ro.config.low_ram=true
    

런처 구성

런처의 기본 배경화면 설정은 라이브 배경화면을 사용하면 안 됩니다. 메모리가 부족한 기기에서는 라이브 배경화면을 사전 설치하면 안 됩니다.

커널 구성

직접 회수를 줄이기 위해 커널/ActivityManager 미세 조정

직접 회수는 프로세스 또는 커널이 메모리 페이지를(직접 또는 새 페이지의 결함으로 인해) 할당하려고 하며 커널이 사용 가능한 모든 여유 메모리를 사용했을 때 발생합니다. 이렇게 하려면 커널이 페이지를 확보하는 동안 할당을 차단해야 합니다. 따라서 일반적으로 디스크 I/O가 더티 파일 지원 페이지를 플러싱하거나 lowmemorykiller가 프로세스를 종료할 때까지 기다려야 합니다. 이로 인해 UI 스레드를 포함하여 모든 스레드에서 추가 I/O가 발생할 수 있습니다.

직접 회수를 피하기 위해 커널에는 kswapd 또는 백그라운드 회수를 트리거하는 워터마크가 있습니다. 이는 페이지 확보를 시도하는 스레드입니다. 따라서 다음에 실제 스레드를 할당할 때 빠르게 할당에 성공할 수 있습니다.

백그라운드 회수를 트리거하는 기본 임계값은 2GB 기기의 경우 약 2MB, 512MB 기기의 경우 636KB 정도로 매우 낮습니다. 커널은 백그라운드 회수 시 불과 몇 MB의 메모리만 회수합니다. 즉, 몇 MB 이상 빠르게 할당하는 프로세스는 빠르게 직접 회수됩니다.

조정 가능한 커널에 대한 지원은 Android-3.4 커널 브랜치에 92189d47f66c67e5fd92eafaa287e153197a454f 패치로 추가되었습니다('조정 가능한 여유 KB 추가'). 기기의 커널에 대해 이 패치를 선별하면 ActivityManager가 메모리의 전체 화면 32bpp 버퍼 3개를 여유 버퍼로 유지하게 시도하도록 해당 커널에 명령할 수 있습니다.

이러한 임계값은 config.xml 프레임워크를 사용하여 구성할 수 있습니다.

    <!-- Device configuration setting the /proc/sys/vm/extra_free_kbytes tunable
    in the kernel (if it exists).  A high value will increase the amount of memory
    that the kernel tries to keep free, reducing allocation time and causing the
    lowmemorykiller to kill earlier.  A low value allows more memory to be used by
    processes but may cause more allocations to block waiting on disk I/O or
    lowmemorykiller.  Overrides the default value chosen by ActivityManager based
    on screen size.  0 prevents keeping any extra memory over what the kernel keeps
    by default.  -1 keeps the default. -->
    <integer name="config_extraFreeKbytesAbsolute">-1</integer>
    
    <!-- Device configuration adjusting the /proc/sys/vm/extra_free_kbytes
    tunable in the kernel (if it exists).  0 uses the default value chosen by
    ActivityManager.  A positive value  will increase the amount of memory that the
    kernel tries to keep free, reducing allocation time and causing the
    lowmemorykiller to kill earlier.  A negative value allows more memory to be
    used by processes but may cause more allocations to block waiting on disk I/O
    or lowmemorykiller.  Directly added to the default value chosen by
    ActivityManager based on screen size. -->
    <integer name="config_extraFreeKbytesAdjust">0</integer>
    

LowMemoryKiller 미세 조정

ActivityManager는 각 우선순위 수준 버킷에서 프로세스를 실행하는 데 필요한 파일 지원 페이지(캐시된 페이지)의 작업 세트에 대한 기대치를 충족하도록 LowMemoryKiller의 임계값을 구성합니다. 기기에 작업 세트에 대한 필요량이 큰 경우(예: 공급업체 UI가 더 많은 메모리를 필요로 하거나 더 많은 서비스가 추가된 경우) 임계값을 늘릴 수 있습니다.

파일 지원 페이지에 너무 많은 메모리가 예약되어 있으면 임계값이 줄어들 수 있습니다. 따라서 캐시가 너무 작아져 디스크 스래싱이 발생하기 훨씬 전에 백그라운드 프로세스가 종료됩니다.

    <!-- Device configuration setting the minfree tunable in the lowmemorykiller
    in the kernel.  A high value will cause the lowmemorykiller to fire earlier,
    keeping more memory in the file cache and preventing I/O thrashing, but
    allowing fewer processes to stay in memory.  A low value will keep more
    processes in memory but may cause thrashing if set too low.  Overrides the
    default value chosen by ActivityManager based on screen size and total memory
    for the largest lowmemorykiller bucket, and scaled proportionally to the
    smaller buckets.  -1 keeps the default. -->
    <integer name="config_lowMemoryKillerMinFreeKbytesAbsolute">-1</integer>
    
    <!-- Device configuration adjusting the minfree tunable in the
    lowmemorykiller in the kernel.  A high value will cause the lowmemorykiller to
    fire earlier, keeping more memory in the file cache and preventing I/O
    thrashing, but allowing fewer processes to stay in memory.  A low value will
    keep more processes in memory but may cause thrashing if set too low.  Directly
    added to the default value chosen by          ActivityManager based on screen
    size and total memory for the largest lowmemorykiller bucket, and scaled
    proportionally to the smaller buckets. 0 keeps the default. -->
    <integer name="config_lowMemoryKillerMinFreeKbytesAdjust">0</integer>
    

zram으로 스왑

zram 스왑을 사용하면 메모리 페이지를 압축하여 동적으로 할당된 메모리 스왑 영역에 넣음으로써 시스템에서 사용 가능한 메모리 양을 늘릴 수 있습니다. 이렇게 하면 메모리가 약간 늘어나는 대신 CPU 시간이 줄어들기 때문에 zram 스왑이 시스템에 미치는 성능 영향을 측정하는 경우 주의해야 합니다.

Android는 여러 수준에서 zram으로 스왑을 처리합니다.

  • 먼저, zram 스왑을 효과적으로 사용하려면 다음 커널 옵션을 사용 설정해야 합니다.
    • CONFIG_SWAP
    • CONFIG_CGROUP_MEM_RES_CTLR
    • CONFIG_CGROUP_MEM_RES_CTLR_SWAP
    • CONFIG_ZRAM
  • 그런 다음 fstab에 다음과 같은 행을 추가해야 합니다.
        /dev/block/zram0 none swap defaults zramsize=<size in bytes>,swapprio=<swap partition priority>
        
    • zramsize는 필수이며 zram 영역에서 유지하려는 비압축 메모리의 양을 나타냅니다. 일반적으로 30~50% 범위의 압축 비율이 관찰됩니다.
    • swapprio는 스왑 영역이 두 개 이상이 아닌 경우에만 필요합니다.

    SELinux에서 적절하게 처리할 수 있도록 연결된 블록 기기의 라벨을 기기별 sepolicy/file_contexts에서 swap_block_device로 지정합니다.

        /dev/block/zram0 u:object_r:swap_block_device:s0
        
  • 기본적으로 Linux 커널은 한 번에 8페이지의 메모리에서 스와핑합니다. zram을 사용하는 경우 한 번에 한 페이지씩 읽기로 인해 증가되는 비용은 무시할 수 있으며 기기에서 메모리 부족이 심한 경우 유용할 수 있습니다. 한 번에 한 페이지만 읽으려면 init.rc에 다음을 추가합니다.
        write /proc/sys/vm/page-cluster 0
        
  • init.rc에서 mount_all /fstab.X 행 뒤에 다음을 추가합니다.
        swapon_all /fstab.X
        
  • 메모리 cgroup은 커널에서 이 기능이 사용 설정된 경우 부팅 시 자동으로 구성됩니다.
  • 메모리 cgroup을 사용할 수 있는 경우 ActivityManager는 우선순위가 낮은 스레드를 다른 스레드보다 스왑 가능성이 더 높은 것으로 표시합니다. 메모리가 필요한 경우 Android 커널이 메모리 페이지를 zram 스왑으로 마이그레이션하기 시작하여 ActivityManager가 표시한 메모리 페이지에 더 높은 우선순위를 부여합니다.

Carveout, Ion, CMA(연속 메모리 할당)

메모리가 부족한 기기의 경우 carveout을 염두에 둬야 하는데, 특히 안전한 동영상 재생을 위한 carveout과 같이 충분히 사용되지 않는 carveout이 여기에 해당됩니다. 하드웨어의 정확한 요구사항에 따라 달라지는 carveout 리전의 영향을 최소화할 수 있는 몇 가지 솔루션이 있습니다.

하드웨어가 비연속 메모리 할당을 허용하면 Ion 시스템 힙이 시스템 메모리의 메모리 할당을 허용하므로 carveout이 필요하지 않습니다. 또한 Ion은 주변 장치에 대한 TLB(Translation Lookaside Buffer) 압력을 없애기 위해 대규모 할당을 시도합니다. 메모리 리전이 인접해 있어야 하거나 특정 IP 주소 범위에 한정되는 경우 CMA를 사용할 수 있습니다.

이렇게 하면 시스템에서 이동 가능한 페이지에도 사용할 수 있는 carveout이 생성됩니다. 리전이 필요한 경우 이동 가능한 페이지가 해당 리전 밖으로 마이그레이션되어 시스템에서는 사용 가능한 경우 큰 carveout을 다른 용도로 사용할 수 있습니다. CMA는 Ion CMA 힙과 함께 직접 사용할 수 있습니다.

앱 최적화 도움말

  • 앱의 메모리 관리 및 다음 블로그 게시물을 살펴보세요.
  • development/tools/findunused를 사용하여 사전 설치된 앱에서 사용하지 않는 애셋을 삭제합니다(이렇게 하면 앱의 크기를 줄일 수 있음).
  • 특히 애셋에 투명 영역이 있는 경우에는 애셋에 PNG 형식을 사용합니다.
  • 네이티브 코드를 작성하는 경우 malloc/memset 대신 calloc()를 사용합니다.
  • Parcel 데이터를 디스크에 쓰고 나중에 읽는 코드는 사용하지 마세요.
  • 설치된 모든 패키지를 구독하는 대신 SSP 필터링을 사용합니다. 다음과 같은 필터링을 추가합니다.
        <data android:scheme="package" android:ssp="com.android.pkg1" />
        <data android:scheme="package" android:ssp="com.myapp.act1" />
        

Android의 다양한 프로세스 상태 이해

상태 의미 세부정보
SERVICE
SERVICE_RESTARTING
앱 관련 이유로 백그라운드에서 실행되는 앱입니다. SERVICESERVICE_RESTARTING은 앱이 백그라운드에서 너무 많이 실행될 때 가장 일반적으로 발생하는 문제입니다. %duration * pss 또는 %duration을 'badness' 측정 항목으로 사용합니다. 이러한 앱이 전혀 실행되지 않는 경우가 이상적입니다.
IMPORTANT_FOREGROUND
RECEIVER
앱이 백그라운드에서 실행되며 사용자와 직접 상호 작용하지 않습니다. 이러한 상태는 시스템에 메모리 로드를 추가합니다. 이러한 프로세스를 정렬하려면 (%duration * pss) 'badness' 값을 사용합니다. 하지만 이러한 앱 중 다수는 적절한 이유로 실행됩니다. pss의 크기는 메모리 로드에서 중요한 부분입니다.
PERSISTENT 영구 시스템 프로세스입니다. pss를 추적하여 프로세스가 너무 커지는지 확인하세요.
TOP 사용자가 현재 상호 작용하고 있는 프로세스입니다. 여기서 pss는 사용 중 앱이 생성하는 메모리 로드의 양을 표시하는 중요한 측정항목입니다.
HOME
CACHED_EMPTY
다시 필요한 경우에 대비해 시스템에서 보관하는 프로세스입니다. 이러한 프로세스는 언제든지 자유롭게 종료하고 필요할 경우 다시 만들 수 있습니다. 메모리 상태(정상, 보통, 부족, 심각)는 시스템에서 실행 중인 프로세스 수에 따라 계산됩니다. 이러한 프로세스의 주요 측정항목은 pss입니다. 이 상태에서 총 프로세스 수를 최대로 유지하려면 프로세스가 메모리 저장 공간을 최대한 줄여야 합니다. 이 상태에서 정상적으로 작동하는 앱은 일반적으로 TOP 상태에서 작동하는 경우보다 pss 저장 공간이 현저하게 작습니다.
CACHED_ACTIVITY
CACHED_ACTIVITY_CLIENT
TOP과 비교할 때 앱이 백그라운드로 메모리를 얼마나 잘 해제하는지 보여줍니다. CACHED_EMPTY 상태를 제외하면 사용자와의 상호 작용 외에도 몇 가지 이유로 프로세스가 시작된 상황을 제거하기 때문에 이 데이터가 개선됩니다. 따라서 사용자 관련 활동 시 CACHED_EMPTY가 가져온 UI 오버헤드를 처리할 필요가 없습니다.

분석

앱 시작 시간 분석

앱 시작 시간을 분석하려면 $ adb shell am start -P 또는 --start-profiler를 실행하고 앱을 시작합니다. 프로파일러는 프로세스가 zygote에서 분기된 후 코드가 포크로 로드되기 전에 시작됩니다.

버그 신고를 사용하여 분석

버그 신고에는 batterystats, netstats, procstatsusagestats 등 디버깅에 사용할 수 있는 여러 서비스가 포함됩니다. 신고에는 다음과 같은 행이 포함될 수 있습니다.

    ------ CHECKIN BATTERYSTATS (dumpsys batterystats --checkin) ------
    7,0,h,-2558644,97,1946288161,3,2,0,340,4183
    7,0,h,-2553041,97,1946288161,3,2,0,340,4183
    

영구 프로세스 확인

영구 프로세스를 확인하려면 기기를 재부팅하고 해당 프로세스를 확인합니다. 그런 다음 몇 시간 동안 기기를 실행하고 프로세스를 다시 확인합니다. 두 확인 사이에는 오랫동안 실행 중인 프로세스가 없어야 합니다.

수명 테스트 실행

수명 테스트를 실행하려면 기기를 더 오랫동안 실행하고 프로세스의 메모리를 추적하여 메모리가 증가했는지 아니면 일정하게 유지되고 있는지 확인합니다. 그런 다음 표준 사용 사례를 만들고 이러한 시나리오에서 수명 테스트를 실행합니다.