Android LMK, OOM

  • by


안녕하세요 유초코 🙂

안드로이드의 LMK (low-memory killer)에 대해 알기보다 OOM과 LMK가 어떤 관계인지 이야기 해 보려고이 테마를 가져 왔습니다 🙂

첫째, 안드로이드 플랫폼에서 프로세스간에 메모리를 할당하는 방법할 것인지 알아보십시오. LMK와 OOM에 대해 배우자!

메모리 타입

첫째, 안드로이드 장치는 RAM, zRAM, Storage의 세 가지 유형의 메모리를 가지고 있으며 CPU와 GPU는 모두 같은 RAM에 액세스합니다.

여기서 RAM(Random Access Memory)은 시스템의 단기 데이터 스토리지로, 정보에 빠르게 액세스할 수 있도록 컴퓨터가 실시간으로 사용하는 정보를 저장하는 공간이며, 시스템에서 많은 프로그램을 실행할수록 더 많은 메모리가 필요합니다.


  • RAM: 가장 빠른 메모리 유형이지만 크기가 제한된 고급 리소스입니다.

  • zRAM: RAM의 파티션으로 스왑 공간으로 사용됩니다.

    모든 것이 zRAM에 배치되면 압축되고 zRAM에서 복사되면 압축이 풀립니다.

    이 부분의 RAM은 페이지가 zRAM에 들어가거나 zRAM에서 나올 때 크기가 커지거나 작아집니다.

  • Storage: 파일 시스템, 모든 앱 라이브러리, 플랫폼에 포함된 객체 코드 등의 영구 데이터를 모두 포함합니다.

메모리 페이지

RAM은 페이지로 분할됩니다(일반적으로 각 페이지는 4KB)

여기서 페이지는 free(사용 가능) 또는 used(사용됨)로 간주됩니다.

사용 가능한 페이지는 사용되지 않는 RAM이고 사용되는 페이지는 시스템에서 사용되는 RAM입니다.

RAM은 다음과 같이 그룹화할 수 있습니다.

사용 가능한 페이지와 사용되는 페이지의 비율은 시스템에서 적극적으로 RAM을 관리하기 때문에 시간이 지남에 따라 변경되며 개념이 메모리 부족 상황을 관리하는 데 핵심입니다.

  • Cached: 리포지토리의 파일로 지원되는 메모리(예: 코드 또는 메모리에 매핑된 파일).
    • Private: 단일 프로세스에서 소유 및 공유되지 않음
    • Clean: 리포지토리의 파일의 변경되지 않은 복사본입니다.

      kswapd에서 삭제하면 사용 가능한 메모리를 늘릴 수 있습니다.

    • Dirty: 리포지토리의 파일의 수정된 복사본입니다.

      kswapd를 통해 zRAM으로 이동하거나 zRAM으로 압축하여 사용 가능한 메모리를 늘릴 수 있습니다.

    • 공유: 여러 프로세스에서 사용합니다.

    • Clean: 리포지토리의 파일의 변경되지 않은 복사본입니다.

      kswapd에서 삭제하여 사용 가능한 메모리를 늘릴 수 있습니다.

    • Dirty: 리포지토리의 파일의 수정된 복사본입니다.

      kswapd를 통해 또는 msync()munmap()를 명시적으로 사용하여 리포지토리의 파일로 변경 내용을 다시 작성하여 사용 가능한 메모리를 늘릴 수 있습니다.

  • Anonymous: storage 파일로 지원되지 않는 메모리입니다.

    MAP_ANONYMOUS
    • Dirty: kswapd를 통해 zRAM으로 이동하거나 zRAM으로 압축하여 사용 가능한 메모리를 늘릴 수 있습니다.

  • 플래그가 설정됨 mmap()에 의해 할당됩니다).

메모

클린 페이지에는 리포지토리에 있는 파일(또는 파일의 일부)의 정확한 복사본이 포함되어 있습니다.

클린 페이지에 파일의 정확한 복사본이 더 이상 포함되지 않으면 더티 페이지가 됩니다(예: 응용 프로그램 작업의 결과로). 클린 페이지는 리포지토리의 데이터를 사용하여 항상 재생성할 수 있으므로 삭제할 수 있습니다.

그러나 더티 페이지는 삭제할 수 없으며 삭제하면 데이터가 손실될 수 있습니다.

LMK(Low memory management)

Android에는 메모리 부족 상황을 처리하는 두 가지 기본 메커니즘이 있습니다.

커널 스왑 데몬(커널 스왑 데몬) 및 low-memory killer(로우 메모리 킬러)가 있습니다.

커널 스왑 데몬

커널 스왑 데몬(kswapd)은 Linux 커널의 일부이며 사용된 메모리를 사용 가능한 메모리로 변환합니다.

데몬은 장치의 사용 가능한 메모리가 부족할 때 활성화되고 Linux 커널은 사용 가능한 메모리의 낮은 임계값과 높은 임계값을 유지합니다.

사용 가능한 메모리가 낮은 임계값 아래로 떨어지면 kswapd가 메모리 복구를 시작하고 사용 가능한 메모리가 높은 임계 값에 도달하면 kswapd는 메모리 복구를 중지합니다.

이전에는 메모리 페이지를 분리하고 수정되지 않은 복사 클린 페이지와 수정된 복사 더티 페이지가 있었습니다.

두 페이지에 따라 kswapd의 동작은 다음과 같습니다.

클린 페이지


kswapd는 클린 페이지를 삭제하고 회수할 수 있습니다.

프로세스가 삭제된 클린 페이지를 처리하려고 하면, 시스템은 리포지토리에서 RAM으로 페이지를 복사하고 이를 demand paging이라고 합니다.

더티 페이지


kswapd는 캐시 된 개인 dirty 페이지와 anonymous dirty 페이지를 zRAM으로 이동할 수 있으며 zRAM에서 이동 한 페이지를 압축하여 RAM에서 사용할 수있는 메모리 (사용 가능한 페이지)를 확보합니다.

프로세스가 zRAM의 더티 페이지를 터치하려고 하면 페이지가 압축 해제되고 다시 RAM으로 이동하고 압축된 페이지와 연관된 프로세스가 종료되면 페이지가 zRAM에서 제거됩니다.

kswapd 시스템에 충분한 메모리를 확보할 수 없는 경우가 많습니다.

사용 가능한 메모리 양이 특정 임계값 아래로 떨어지면 시스템은 onTrimMemory()를 사용하여 메모리가 부족하여 할당량을 줄여야 한다고 앱에 알립니다.

이 방법으로 충분하지 않으면 커널이 메모리를 해제하려고 시도하고 프로세스를 종료하기 시작하고, 이 작업을 수행하기 위해 로우 메모리 킬러(low-memory killer, LMK)를 사용합니다.

Low-memory killer

LMK는 oomadjscore“메모리 부족” 점수를 사용하여 종료할 프로세스를 결정합니다.

실행 중인 프로세스의 우선 순위를 결정하고 최고 점수를 얻은 프로세스가 먼저 종료되고 백그라운드 앱이 먼저 종료되고 시스템 프로세스가 마지막으로 종료됩니다.

다음 표는 LMK 점수 카테고리가 높은 점수에서 낮은 점수로 나열됩니다.


  • Background app: 이전에 실행 중이며 현재 활성화되지 않은 앱, LMK는 oom_adj_score가 가장 높은 백그라운드 앱에서 먼저 종료됩니다.

  • Previous app: 최근에 사용한 백그라운드 앱, 이전 앱은 백그라운드 앱보다 우선 순위가 높고 점수가 낮습니다.

    (사용자가 백그라운드 앱 이전의 앱으로 전환할 가능성이 높기 때문에)
  • Home app: 런처 앱입니다.

    해당 앱을 종료하면 배경 화면이 사라집니다.

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

  • Perceptible app: 사용자가 어떤 식으로든 인식할 수 있는 전경에 없는 앱(예: 작은 UI를 표시하는 검색 프로세스를 실행하거나 음악을 듣습니다).
  • Foreground app: 현재 사용중인 앱, 포그라운드 앱을 종료하면 애플리케이션이 비정상적으로 종료되는 것처럼 보이고 사용자가 단말기에 문제가 있다고 생각할 수 있다.

  • Persistent(서비스): 텔레포니, Wi-Fi 등 기기의 핵심 서비스입니다.

  • System(시스템 프로세스): 프로세스가 종료되면 장치가 재부팅된 것처럼 보일 수 있습니다.

  • Native: 시스템에서 사용되는 매우 낮은 수준의 프로세스(예: kswapd).

LMK가 하는 것은 위와 같았다.

그럼 OOM이란?

OOM은 Linux 커널에서 이전부터 계속 존재해온 메모리 관리 모듈입니다.

OOM이 휴대 단말에 들어가는 시스템에는 적합하지 않다고 생각하고 OOM이 발생하지 않도록 메모리를 관리하려고 Android로 만든 것이 LMK입니다.

즉, Android에는 LMK와 OOM이 있으며 작동하지만 OOM이 발생하면 파급 효과가 크기 때문에 이러한 것을 방지하기 위해 작동하는 것이 LMK입니다.

..

그렇다면 OOM이 무엇이며 파급 효과가 크다는 것일까요?

OOM

jvm이 메모리 부족으로 오브젝트를 할당할 수 없고, garbage collector가 메모리를 사용할 수 없게 되었을 때에 발생합니다.

OOM Killer : OOM이 발생하지 않도록 적은 프로세스죽인다 많은 메모리을 확보합니다.

따라서 특정 프로세스가 죽지 않음을 보장할 수 없습니다.

OOM 발생 원인 : 커널은 Virtual memory 를 이용한 메모리 할당을 하므로, 실제로 이용 가능한 physical 메모리보다 큰 프로그램 size 의 메모리를 할당할 수 있습니다.

즉, 프로그램에서 즉시 사용하지 않는 메모리는 나중에 할당되므로 실제 사용 가능한 메모리를 초과하는 프로세스가 로드할 수 있습니다(이를 오버커밋이라고 함). 따라서 Out of memory가 발생합니다.

Android에서 OutOfMemoryError가 발생하는 가장 많은 경우 비트맵 로드 따라서 Android 3.0 이하에서는 Bitmap의 메모리가 Dalvik VM에 할당되지 않았으며 Native Heap 영역에 할당되었습니다.

따라서 Bitmap이 VM의 GC(Garbage Collecting) 대상이 되지 않고 OOM이 발생하게 되었습니다.

  • Java 비트맵 객체는 참조가 없을 때 GC에 의해 회수되지만 네이티브 힙 영역은 GC 실행 영역 외부이므로 메모리가 사라지는 시점이 다릅니다.

Dalvik VM은 첫 번째 작업에 필요한 만큼 프로세스에 힙을 할당하고 할당된 메모리보다 더 많은 메모리가 필요할 때마다 Dalvik Footfrint도 증가합니다.

여기에서 증가한 Dalvik Footfrint는 감소하지 않으므로, 외부 힙의 크기가 증가하면 OOM이 발생할 수 있습니다.

이러한 문제로 인해 Honeycomb에서는 네이티브 비트맵 객체를 저장하기 위한 외부 영역을 없애고 dalvik 힙만 남기고 비트맵 픽셀 데이터 자체도 dalvik 힙 영역에 저장했습니다.

그리고 GC(Garbage Collecting)도 액세스할 수 있게 되었습니다…

마지막으로 요약하면 Android는 OOM Killer와 LMK를 모두 사용합니다!
그리고 OOM 상황에서는 치명적인 상황이 발생하기 때문에 Android에서 LMK라는 모듈을 추가하여 OOM 상황이 발생하기 전에 이것을 방지합니다.

참조


https://developer.android.com/topic/performance/memory-management?hl=ko