Skip to content

Most visited

Recently visited

navigation

RAM 사용량 조사

Android 앱을 개발할 때는 앱의 RAM 사용량에 항상 주의를 기울이세요. Dalvik 및 ART 런타임은 루틴 가비지 수집(GC)을 수행하지만, 그래도 앱이 메모리를 할당하고 해제하는 시점과 위치를 이해할 필요가 있습니다. Android 운영체제가 앱 사이에서 빠르게 전환할 수 있는 안정적인 사용자 환경을 제공하려면, 사용자가 앱과 상호 작용하지 않을 때는 앱이 메모리를 불필요하게 사용하지 않도록 해야 합니다.

개발 중에 앱 메모리 관리를 위한 모든 모범 사례를 따르더라도 메모리 누수를 일으키는 객체가 있거나 다른 메모리 버그가 있을 수 있습니다. 개발하는 앱이 메모리를 가능한 한 적게 사용하도록 확실히 조치하는 유일한 방법은 여기서 설명하는 도구로 앱의 메모리 사용량을 분석하는 것입니다.

로그 메시지 해석

앱의 메모리 사용량 조사를 가장 간단하게 시작할 수 있는 곳은 런타임 로그 메시지입니다. 가끔 GC가 발생할 때 logcat에서 메시지를 볼 수 있습니다.

Dalvik 로그 메시지

(ART가 아니라) Dalvik에서는 모든 GC가 각각 logcat에 다음 정보를 출력합니다.

D/dalvikvm: <GC_Reason> <Amount_freed>, <Heap_stats>, <External_memory_stats>, <Pause_time>

예시:

D/dalvikvm( 9050): GC_CONCURRENT freed 2049K, 65% free 3571K/9991K, external 4703K/5261K, paused 2ms+2ms
GC Reason(GC 이유)
GC를 트리거한 이유와 가비지 수집의 종류에 대한 설명입니다. 다음과 같은 이유가 나타날 수 있습니다.
GC_CONCURRENT
힙이 채우기 시작하면서 메모리를 해제하는 동시 GC입니다.
GC_FOR_MALLOC
힙이 이미 가득 찼을 때 앱이 메모리 할당을 시도했기 때문에 GC가 발생한 것이므로, 시스템이 앱을 중지하고 메모리를 회수해야 했습니다.
GC_HPROF_DUMP_HEAP
힙 분석을 위해 HPROF 파일 생성을 요청할 때 발생하는 GC입니다.
GC_EXPLICIT
gc()를 호출할 때와 같이 명시적인 GC로서, 필요하다면 GC가 자동으로 실행될 것이라 믿고 이러한 GC를 호출하지는 마세요.
GC_EXTERNAL_ALLOC
API 레벨 10 이하에서만 발생하는 GC이며, 그 이상의 버전에서는 Dalvik 힙에서 모든 메모리를 할당합니다. 외부에서 할당되는 메모리에 대한 GC입니다(예: 네이티브 메모리 또는 NIO 바이트 버퍼에 저장되는 픽셀 데이터).
Amount freed(확보한 크기)
이 GC를 통해 확보한 메모리의 크기입니다.
Heap stats(힙 통계)
힙에서 회수한 비율로, (라이브 객체 수)/(총 힙 크기)로 계산됩니다.
External memory stats(외부 메모리 통계)
API 레벨 10 이하에서 외부 할당된 메모리로, (할당된 메모리의 크기) / (수집이 발생하게 되는 한계)입니다.
Pause time(일시 중지 횟수)
힙이 클수록 일시 중지 횟수가 많아집니다. 동시 일시 중지 횟수는 두 번의 일시 중지를 표시하는데, 하나는 수집 시작 시점이고 다른 하나는 거의 종료 시점입니다.

이러한 로그 메시지가 누적되는 동안 힙 통계에서 증가하는 부분이 있는지 찾아보세요(위 예시에서는 3571K/9991K 값). 이 값이 계속 증가한다면 메모리 누수가 원인일 수 있습니다.

ART 로그 메시지

Dalvik과는 달리, ART는 명시적으로 요청하지 않은 GC에 대한 메시지는 로그에 기록하지 않습니다. GC는 느린 것으로 판단될 때만 출력됩니다. 더 정확히 말하자면, GC 일시 중지 시간이 5ms를 초과하거나 GC 지속 시간이 100ms를 초과하는 경우가 이에 해당합니다. 앱이 일시 중지를 인지할 수 있는 프로세스 상태가 아닐 경우에는 어떤 GC도 느린 것으로 판단되지 않습니다. 명시적인 GC는 항상 로그에 기록됩니다.

ART는 가비지 수집 로그 메시지에 다음 정보를 포함합니다.

I/art: <GC_Reason> <GC_Name> <Objects_freed>(<Size_freed>) AllocSpace Objects, <Large_objects_freed>(<Large_object_size_freed>) <Heap_stats> LOS objects, <Pause_time(s)>

예시:

I/art : Explicit concurrent mark sweep GC freed 104710(7MB) AllocSpace objects, 21(416KB) LOS objects, 33% free, 25MB/38MB, paused 1.230ms total 67.216ms
GC Reason(GC 이유)
GC를 트리거한 이유와 가비지 수집의 종류에 대한 설명입니다. 다음과 같은 이유가 나타날 수 있습니다.
Concurrent
앱 스레드를 일시 중단하지 않는 동시 GC입니다. 이 GC는 백그라운드 스레드에서 실행되며 할당을 막지 않습니다.
Alloc
힙이 이미 가득 찼을 때 앱이 메모리 할당을 시도했기 때문에 이 GC가 시작된 것입니다. 이 경우에는 가비지 수집이 할당 스레드에서 발생했습니다.
Explicit
앱이 가비지 수집을 명시적으로 요청했습니다(예: gc() 또는 gc() 호출). Dalvik과 마찬가지로, ART에서는 가급적이면 GC를 믿고 명시적 GC를 요청하지 않는 것이 최선책입니다. 명시적 GC는 할당 스레드를 차단하고 CPU 사이클을 불필요하게 낭비하므로 요청하지 않는 것이 좋습니다. 명시적인 GC로 인해 다른 스레드가 선점될 경우에는 쟁크(앱의 버벅거림, 끊김 또는 중단)가 발생할 수도 있습니다.
NativeAlloc
Bitmap 또는 RenderScript 할당 객체와 같은 네이티브 할당에 따른 네이티브 메모리 압력으로 인해 발생한 가비지 수집입니다.
CollectorTransition
힙 전환으로 인해 발생한 수집으로, 이 경우에는 런타임에 GC를 전환한 것이 원인입니다. 수집기 전환 과정은 사용 가능 목록에 나오는 공간에서 범프 포인터 공간으로(또는 그 반대로) 모든 객체를 복사하는 작업으로 구성됩니다. 현재는 RAM 사양이 낮은 기기에서 앱이 프로세스 상태를 일시 중지를 인식할 수 있는 상태에서 인식할 수 없는 상태로(또는 그 반대로) 변경할 때만 수집기 전환이 발생합니다.
HomogeneousSpaceCompact
동종 공간 압축은 사용 가능 목록 공간 간의 압축으로, 보통 앱이 일시 중지를 인식할 수 없는 프로세스 상태로 이동할 때 발생합니다. 이 작업을 수행하는 주된 이유는 RAM 사용량 감소와 힙 조각 모음을 위한 것입니다.
DisableMovingGc
이는 실제 GC 이유는 아니지만, 동시 힙 압축이 이루어지는 동안 GetPrimitiveArrayCritical을 사용하는 바람에 수집이 차단되었음을 나타냅니다. 일반적으로, GetPrimitiveArrayCritical은 수집기 이동을 제한하므로 불가피한 경우가 아니라면 사용하지 마세요.
HeapTrim
이 역시 GC 이유는 아니지만, 힙 트림을 마칠 때까지 수집이 차단되었습니다.
GC Name(GC 이름)
ART에서는 여러 가지 다양한 GC가 실행될 수 있습니다.
Concurrent mark sweep (CMS)
이미지 공간 이외의 모든 공간을 회수하고 수집하는 완전한 힙 수집기입니다.
Concurrent partial mark sweep
이미지 및 zygote 공간 이외의 공간을 전부 수집하는 거의 완전한 힙 수집기입니다.
Concurrent sticky mark sweep
마지막 GC 이후로 할당된 객체만 회수할 있는 세대별 수집기입니다. 이 가비지 수집이 더 빠르고 일시 중지 횟수도 적으므로 전체 또는 부분 마크 스윕보다 더 자주 실행됩니다.
Marksweep + semispace
(힙 조각 모음을 위한) 동종의 공간 압축뿐 아니라 힙 전환에도 사용되는 비동시 방식의 복사 GC입니다.
Objects freed(회수한 객체 수)
크지 않은 객체 공간에서 이 GC를 통해 회수한 객체의 수입니다.
Size freed(회수한 공간의 크기)
크지 않은 객체 공간에서 이 GC를 통해 회수한 바이트의 수입니다.
Large objects freed(회수한 큰 객체)
이 가비지 수집으로 회수한 큰 객체 공간에 있는 객체의 수입니다.
Large object size freed(회수한 큰 객체의 크기)
이 가비지 수집으로 회수한 큰 객체 공간에 있는 바이트의 수입니다.
Heap stats(힙 통계)
회수한 비율로, (라이브 객체 수)/(총 힙 크기)로 계산됩니다.
Pause times(일시 중지 횟수)
일반적으로 일시 중지 횟수는 GC 실행 중에 수정한 객체 참조의 개수에 비례합니다. 현재 ART CMS GC는 GC 종료 시점 근처에서 한 번만 일시 중지됩니다. 이동 GC에는 GC 지속 시간 중 대부분의 시간 동안 지속되는 긴 일시 중지가 있습니다.

Logcat에 다량의 GC가 보일 경우에는 힙 통계에서 증가되는 부분이 있는 찾아보세요(위 예시에서는 25MB/38MB 값). 이 값이 계속 증가하고 있고 작아질 것으로 보이지 않는다면 메모리 누수 가능성이 있습니다. 또는 "Alloc" 이유로 인한 GC가 보일 경우에는 이미 힙 용량 근처에 도달해 있는 것이므로 이내 OOM 예외가 발생할 것을 예상할 수 있습니다.

Android Monitor 액세스

  1. 연결된 기기나 에뮬레이터에서 앱을 시작합니다.
  2. View > Tool Windows > Android Monitor를 선택합니다.
  3. Android Monitor의 왼쪽 위 모서리에서 Monitors 탭을 선택합니다.

    그림 1. Android Monitor와 모니터의 세 요소인 Memory, CPUGPU. Android Studio에서 Android Monitor 패널을 세로 방향으로 확대하면 Network 모니터를 볼 수 있습니다.

힙 덤프 캡처

힙 덤프는 앱의 힙에 있는 모든 객체의 스냅샷입니다. 힙 덤프는 jhat과 같은 분석 도구로 업로드할 수 있는 HPROF라는 바이너리 형식으로 저장됩니다. 앱의 힙 덤프는 앱의 힙에 대한 전반적인 상태 정보를 포함하고 있으므로 힙 업데이트를 보는 동안 확인했을 수도 있는 문제를 추적할 수 있습니다.

  1. Memory 모니터 상단에서 Dump Java Heap을 클릭합니다 .

    Android Studio에서 파일 이름이 application-id_yyyy.mm.dd_hh.mm.hprof인 힙 스냅샷 파일이 생성되어 열리고 Captures 탭의 Heap Snapshot 목록에 추가됩니다.

  2. Captures 탭에서 파일을 마우스 오른쪽 버튼으로 클릭하고 Export to standard .hprof를 선택합니다.

참고: 덤프가 생성되는 시점에 대해 더 정확하게 확인해야 할 경우에는 dumpHprofData()를 호출하여 앱 코드의 중요한 지점에서 힙 덤프를 생성할 수 있습니다.

힙 업데이트 보기

앱과 상호 작용하는 동안 Android Monitor를 사용하여 앱의 힙에 대한 실시간 업데이트를 볼 수 있습니다. 실시간 업데이트는 여러 가지 다양한 앱 작업을 위해 할당되는 메모리의 양에 대한 정보를 제공합니다. 이 정보를 바탕으로 어떤 작업에서 너무 많은 메모리를 사용하는지, 그리고 메모리 사용량 감소를 위해 어떤 작업을 조정할 필요가 있는지 결정할 수 있습니다.

  1. 앱과 상호 작용하면서 Memory 모니터에서 FreeAlloated 메모리를 확인합니다.
  2. Dump Java Heap을 클릭합니다 .
  3. Captures 탭에서 힙 스냅샷 파일을 두 번 클릭하여 HPROF 뷰어를 엽니다.
  4. 힙이 할당되도록 하려면 앱과 상호 작용하면서 Initiate GC를 클릭합니다 .

앱과 계속 상호 작용하면서 GC를 시작하세요. 각 GC와 함께 힙 할당 업데이트를 살펴보세요. 앱에서 어떤 작업이 너무 많은 메모리 할당의 원인이 되는지, 어느 위치에서 할당을 줄여 리소스를 확보할 수 있을지 파악할 수 있을 것입니다.

힙 덤프 분석

힙 덤프는 자바 HPROF 도구의 힙 덤프와 비슷하긴 하지만 똑같지는 않은 형식으로 제공됩니다. Android 힘 덤프의 주요 차이점은 Zygote 프로세스에 다수 할당된다는 점입니다. Zygote 할당은 모든 앱 프로세스에서 공유되기 때문에 자체적인 힙 분석에는 그다지 중요하지 않습니다.

힙 덤프를 분석하려면 jhat 같은 표준 도구를 사용하면 됩니다. jhat을 사용하려면 HPROF 파일을 Android 형식에서 Java SE HPROF 형식으로 변환해야 합니다. Java SE HPROF 형식으로 변환하려면 ANDROID_SDK/platform-tools/ 디렉토리에 있는 hprof-conv 도구를 사용합니다. 원본 HPROF 파일과 변환된 HPROF 파일을 쓸 위치를 나타내는 두 가지 인수를 포함한 hprof-conv 명령을 실행합니다. 예:

hprof-conv heap-original.hprof heap-converted.hprof

Java SE HPROF 형식을 이해하는 힙 분석 도구로 변환한 파일을 로드할 수 있습니다. 분석 중에 다음과 같은 원인으로 인한 메모리 누수가 있는지 살펴보세요.

  • Activity, Context, View, Drawable, 그리고 Activity 또는 Context 컨테이너에 대한 참조가 있을 수 있는 다른 객체에 대한 수명이 긴 참조
  • Activity 인스턴스를 보유하고 있을 수 있는 비정적 내부 클래스(예: Runnable)
  • 필요 이상으로 긴 객체를 가진 캐시

메모리 할당 추적

메모리 할당을 추적하면 메모리를 지나치게 차지하는 객체가 할당되어 있는 곳을 더욱 정확히 파악할 수 있습니다. Allocation Tracker를 사용하여 구체적인 메모리 사용량을 살펴보고 스크롤 동작처럼 앱에서 중요한 코드 경로를 분석할 수 있습니다.

예를 들어, Allocation Tracker를 사용해 앱에서 목록을 플링하면서 할당을 추적할 수 있을 것입니다. 이러한 추적을 통해 목록 플링에 필요한 메모리 할당, 메모리가 할당된 스레드, 메모리 할당을 시작한 위치를 전부 알아낼 수 있습니다. 이러한 종류의 정보는 실행 경로를 간소화하여 사용자가 해야 할 일을 줄여주므로, 앱과 사용자 인터페이스의 전반적인 사용 환경을 개선하는 효과가 있습니다.

성능에 중요한 코드 경로에서 모든 메모리 할당을 삭제할 필요가 없고 가능하지도 않지만, Allocation Tracker는 코드에 있는 중요한 문제를 파악하는 데 도움이 될 수 있습니다. 예를 들어, 앱에서 그릴 때마다 새 Paint 객체가 생성될 수 있습니다. Paint 객체를 전역 객체로 만들면 간단히 해결되며, 이는 성능 개선에 기여합니다.

  1. 연결된 기기나 에뮬레이터에서 앱을 시작합니다.
  2. Android Studio에서 View > Tool Windows > Android Monitor를 선택합니다.
  3. Android Monitor의 왼쪽 위 모서리에서 Monitors 탭을 선택합니다.
  4. Memory Monitor 툴바에서 Allocation Tracker 를 클릭하여 메모리 할당을 시작합니다.
  5. 앱과 상호작용합니다.
  6. Allocation Tracker 를 다시 클릭하여 할당 추적을 중지합니다.

    Android Studio에서 파일 이름이 application-id_yyyy.mm.dd_hh.mm.alloc인 할당 파일이 생성되어 열리고 Captures 탭의 Allocations 목록에 추가됩니다.

  7. 할당 파일에서 앱의 어떤 작업이 너무 많은 메모리 할당의 원인이 되고 있는지 식별한 후 앱의 어느 위치에서 할당 감소와 리소스 확보를 시도해야 할지 결정합니다.

Allocation Tracker 사용에 대한 자세한 정보는 Allocation Tracker를 참조하세요.

전체 메모리 할당 보기

더 자세히 분석하려면 다음과 같이 adb 명령을 사용해 다양한 유형의 RAM 할당 사이에서 앱의 메모리가 어떻게 나뉘는지 관찰해 볼 수 있습니다.

adb shell dumpsys meminfo <package_name|pid> [-d]

-d 플래그를 지정하면 Dalvik 및 ART 메모리 사용량과 관련된 더 자세한 정보가 출력됩니다.

출력 결과에는 앱의 현재 할당 메모리가 전부 KB 단위로 나열됩니다.

이 정보를 자세히 검사하려면 다음 유형의 할당에 대해 이해하고 있어야 합니다.

개인(클린 및 더티) RAM
개인 RAM은 자신의 프로세스만이 사용 중인 메모리입니다. 이 메모리는 앱의 해당 프로세스를 삭제할 때 시스템에서 확보할 수 있는 분량의 RAM입니다. 일반적으로 이 메모리에서 가장 중요한 부분은 개인 더티 RAM으로, 오직 자신의 프로세스만이 이 메모리를 사용하고 그 콘텐츠는 RAM에만 존재하여 리포지토리로 페이징할 수 없기 때문에 가장 값비싼 메모리인 셈입니다(Android는 스왑을 사용하지 않음). 개발자가 지정하는 모든 Dalvik 및 네이티브 힙 할당은 개인 더티 RAM이 되고, Zygote 프로세스와 공유하는 Dalvik 및 네이티브 할당은 공유 더티 RAM이 됩니다.
PSS(Proportional Set Size)
PSS는 여러 프로세스에 걸친 페이지 공유를 고려한 앱의 RAM 사용 지표입니다. 개발자 자신의 프로세스에 고유한 RAM 페이지는 전부 PSS 값에 직접 영향을 미치는 반면, 다른 프로세스와 공유하는 페이지는 공유하는 양에 비례하는 PSS 값에만 영향을 미칩니다. 예를 들어, 두 프로세스 간에 공유하는 페이지는 각 프로세스의 PSS에 대해 크기의 절반에 해당합니다.

PSS 측정의 좋은 특징 한 가지는 모든 프로세스에 PSS를 추가하여 모든 프로세스에서 사용 중인 실제 메모리를 확인할 수 있다는 점입니다. 즉, PSS는 프로세스의 실제 RAM 가중치와 다른 프로세스의 RAM 사용량 및 사용 가능한 전체 RAM에 대해 비교하기 위한 훌륭한 지표라는 뜻입니다.

아래에 Nexus 5 기기의 Map 프로세스에 대한 출력이 예시로 나와 있습니다. 많은 정보가 나와 있지만 토론할 핵심 사항을 아래에 표시합니다.

adb shell dumpsys meminfo com.google.android.apps.maps -d

참고: 각자의 출력 결과에서 볼 수 있는 정보가 여기에 표시된 것과는 약간 다를 수도 있습니다. 출력 결과 중 일부 세부 정보가 플랫폼 버전마다 다르기 때문입니다.

** MEMINFO in pid 18227 [com.google.android.apps.maps] **
                   Pss  Private  Private  Swapped     Heap     Heap     Heap
                 Total    Dirty    Clean    Dirty     Size    Alloc     Free
                ------   ------   ------   ------   ------   ------   ------
  Native Heap    10468    10408        0        0    20480    14462     6017
  Dalvik Heap    34340    33816        0        0    62436    53883     8553
 Dalvik Other      972      972        0        0
        Stack     1144     1144        0        0
      Gfx dev    35300    35300        0        0
    Other dev        5        0        4        0
     .so mmap     1943      504      188        0
    .apk mmap      598        0      136        0
    .ttf mmap      134        0       68        0
    .dex mmap     3908        0     3904        0
    .oat mmap     1344        0       56        0
    .art mmap     2037     1784       28        0
   Other mmap       30        4        0        0
   EGL mtrack    73072    73072        0        0
    GL mtrack    51044    51044        0        0
      Unknown      185      184        0        0
        TOTAL   216524   208232     4384        0    82916    68345    14570

 Dalvik Details
        .Heap     6568     6568        0        0
         .LOS    24771    24404        0        0
          .GC      500      500        0        0
    .JITCache      428      428        0        0
      .Zygote     1093      936        0        0
   .NonMoving     1908     1908        0        0
 .IndirectRef       44       44        0        0

 Objects
               Views:       90         ViewRootImpl:        1
         AppContexts:        4           Activities:        1
              Assets:        2        AssetManagers:        2
       Local Binders:       21        Proxy Binders:       28
       Parcel memory:       18         Parcel count:       74
    Death Recipients:        2      OpenSSL Sockets:        2

다음은 Dalvik에서 gmail 앱의 기존 dumpsys입니다.

** MEMINFO in pid 9953 [com.google.android.gm] **
                 Pss     Pss  Shared Private  Shared Private    Heap    Heap    Heap
               Total   Clean   Dirty   Dirty   Clean   Clean    Size   Alloc    Free
              ------  ------  ------  ------  ------  ------  ------  ------  ------
  Native Heap      0       0       0       0       0       0    7800    7637(6)  126
  Dalvik Heap   5110(3)    0    4136    4988(3)    0       0    9168    8958(6)  210
 Dalvik Other   2850       0    2684    2772       0       0
        Stack     36       0       8      36       0       0
       Cursor    136       0       0     136       0       0
       Ashmem     12       0      28       0       0       0
    Other dev    380       0      24     376       0       4
     .so mmap   5443(5) 1996    2584    2664(5) 5788    1996(5)
    .apk mmap    235      32       0       0    1252      32
    .ttf mmap     36      12       0       0      88      12
    .dex mmap   3019(5) 2148       0       0    8936    2148(5)
   Other mmap    107       0       8       8     324      68
      Unknown   6994(4)    0     252    6992(4)    0       0
        TOTAL  24358(1) 4188    9724   17972(2)16388    4260(2)16968   16595     336

 Objects
               Views:    426         ViewRootImpl:        3(8)
         AppContexts:      6(7)        Activities:        2(7)
              Assets:      2        AssetManagers:        2
       Local Binders:     64        Proxy Binders:       34
    Death Recipients:      0
     OpenSSL Sockets:      1

 SQL
         MEMORY_USED:   1739
  PAGECACHE_OVERFLOW:   1164          MALLOC_SIZE:       62

보통은 Pss TotalPrivate Dirty 열에만 신경을 쓰면 됩니다. 경우에 따라서는 Private CleanHeap Alloc 열에서도 흥미로운 데이터를 볼 수 있습니다. 잘 관찰해봐야 할 다른 메모리 할당에 대한 추가 정보(행)는 다음과 같습니다.

Dalvik Heap
앱에서 Dalvik 할당이 사용하는 RAM입니다. Pss Total은 모든 Zygote 할당을 포함합니다(위의 PSS 정의에서 설명한 것처럼, 프로세스 사이의 공유로 인해 가중됨). Private Dirty 수는 앱의 힙에만 커밋된 실제 RAM으로, 자체적인 할당과 Zygote에서 앱의 프로세스를 분기한 후로 수정된 모든 Zygote 할당 페이지로 구성됩니다.

참고: Dalvik Other 섹션이 있는 새로운 플랫폼 버전에서는 Dalvik Heap에 대한 Pss TotalPrivate Dirty 수에 JIT(just-in-time) 컴파일 및 GC 기록 관리와 같은 Dalvik 오버헤드가 포함되지 않는 반면, 이전 버전에서는 Dalvik 하의 총 오버헤드가 모두 나열됩니다.

Heap Alloc은 Dalvik 및 네이티브 힙 할당자가 앱에 대해 계속 추적하는 메모리의 크기입니다. 이 값은 Pss TotalPrivate Dirty보다 큰데, 그 이유는 프로세스가 Zygote에서 분기되었고 다른 모든 프로세스와 공유하는 할당을 포함하기 때문입니다.

.so mmap.dex mmap
매핑된 .so(네이티브) 및 .dex(Dalvik 또는 ART) 코드용으로 사용 중인 RAM입니다. Pss Total 수에는 여러 앱 사이에서 공유되는 플랫폼 코드가 포함됩니다. Private Clean은 개발자의 앱 자체 코드입니다. 일반적으로, 실제 매핑되는 크기는 훨씬 더 큽니다. 여기서 RAM은 앱에서 실행한 코드에 대해 현재 RAM에 있어야 하는 것뿐입니다. 하지만 .so mmap에는 큰 개인 더티 RAM이 있는데, 이는 최종 주소로 로드될 때 네이티브 코드를 수정했기 때문입니다.
.oat mmap
여러 앱에서 공통적으로 사용하는 미리 로드된 클래스를 기반으로 하는 코드 이미지가 사용하는 RAM의 크기입니다. 이 이미지는 모든 앱 사이에서 공유되며 특정 앱의 영향을 받지 않습니다.
.art mmap
여러 앱에서 공통적으로 사용하는 미리 로드된 클래스를 기반으로 하는 힙 이미지가 사용하는 RAM의 크기입니다. 이 이미지는 모든 앱 사이에서 공유되며 특정 앱의 영향을 받지 않습니다. ART 이미지는 Object 인스턴스를 포함하고 있지만 힙 크기에는 계산에 넣지 않습니다.
.Heap(-d 플래그를 포함한 경우만)
앱의 힙 메모리 크기입니다. 이 할당에서는 이미지에 있는 객체와 큰 객체의 공간은 제외하지만, zygote 공간과 이동하지 않는 공간은 포함합니다.
.LOS(-d 플래그를 포함한 경우만)
ART 대형 객체 공간에서 사용되는 RAM의 크기입니다. 이는 zygote 대형 객체를 포함합니다. 큰 객체는 12KB보다 큰 완전 원시 배열 할당입니다.
.GC(-d 플래그를 포함한 경우만)
앱에 대한 내부 GC 계정 오버헤드의 크기입니다. 실제로는 이 오버헤드를 줄일 방법이 없습니다.
.JITCache(-d 플래그를 포함한 경우만)
JIT 데이터 및 코드 캐시가 사용하는 메모리의 크기입니다. 일반적으로는 설치 시점에 모든 앱이 컴파일될 것이므로 이 값은 0입니다.
.Zygote(-d 플래그를 포함한 경우만)
zygote 공간에서 사용되는 메모리의 크기입니다. zygote 공간은 기기 시작 중에 생성되며 이 공간에는 결코 할당되지 않습니다.
.NonMoving(-d 플래그를 포함한 경우만)
ART 비이동 공간에서 사용되는 RAM의 크기입니다. 비이동 공간에는 필드와 메서드 같이 이동할 수 없는 특수한 객체가 포함됩니다. 앱에서 사용하는 필드와 메서드 수를 줄여 이 부분을 줄일 수 있습니다.
.IndirectRef(-d 플래그를 포함한 경우만)
ART 간접 참조 테이블에서 사용되는 RAM의 크기입니다. 이 크기는 대개 작지만, 너무 높은 값일 경우 사용하는 로컬 및 전역 JNI 참조의 수를 줄여 크기를 줄일 수 있습니다.
Unknown
시스템에서 더 구체적인 다른 항목 중 하나로 분류할 수 없는 RAM 페이지입니다. 현재, 이 페이지에는 ASLR(Address Space Layout Randomization)로 인해 이 데이터를 수집할 때 도구가 식별할 수 없는 네이티브 할당이 주로 포함되어 있습니다. Dalvik 힙과 마찬가지로, Unknown에 대한 Pss Total에서는 Zygote와의 공유를 고려하고 Private Dirty는 개발자 자신의 앱에만 전용으로 사용되는 알 수 없는 RAM입니다.
TOTAL
프로세스에서 사용하는 총 PSS(Proportional Set Size) RAM입니다. 이는 그 이상의 모든 PSS 필드의 합입니다. 즉, 프로세스의 전체 메모리 가중치를 나타내며, 다른 프로세스 및 사용 가능한 총 RAM과 직접 비교할 수 있습니다.

Private DirtyPrivate Clean은 프로세스 내부의 전체 할당으로, 다른 프로세스와는 공유되지 않습니다. 이와 함께(특히 Private Dirty), TOTAL은 프로세스를 삭제할 때 시스템에서 다시 확보하게 되는 RAM의 크기입니다. 더티 RAM은 개발자가 수정한 페이지이므로 RAM에 커밋된 상태를 유지해야 합니다(스왑이 없으므로). 반면, 클린 RAM은 영구적 파일에서 매핑된 페이지(예: 실행 중인 코드)이므로 얼마 동안 사용하지 않을 경우 페이지 아웃할 수 있습니다.

ViewRootImpl
프로세스에서 활성 상태인 루트 뷰의 개수입니다. 각 루트 뷰는 창과 연결되어 있으므로, 대화상자나 다른 창과 관련된 메모리 누수를 식별하는 데 도움이 될 수 있습니다.
AppContextsActivities
현재 프로세스에서 활성화된 앱 ContextActivity 객체의 개수입니다. 이는 (흔히 있는 일이지만) 정적 참조로 인해 가비지를 수집할 수 없는 누수된 Activity 객체를 빠르게 식별하는 데 도움이 될 수 있습니다. 이러한 객체에는 다른 할당이 다수 연결되어 있는 경우가 많으므로 큰 메모리 누수를 추적하기에 좋은 방법입니다.

참고: View 또는 Drawable 객체는 자신의 시작 위치인 Activity에 대한 참조도 보유하고 있으므로, View 또는 Drawable 객체를 보유하고 있을 때도 앱에서 Activity 누수가 발생할 수 있습니다.

메모리 누수 트리거

위에서 설명한 도구를 사용하는 동안 앱 코드에 대해 가혹할 정도의 스트레스 테스트를 하면서 강제로 메모리 누수를 발생시켜 봐야 합니다. 앱에서 메모리 누수를 유발하는 한 가지 방법은 힙을 검사하기 전에 잠깐 동안 앱이 실행되도록 두는 것입니다. 그러면 조금씩 누수되다가 힙의 할당 상한에 이르게 됩니다. 하지만 누수가 적을수록 앱을 더 오래 실행해야 이러한 현상을 볼 수 있습니다.

다음 중 한 가지 방법으로 메모리 누수를 트리거할 수도 있습니다.

  1. 기기를 다양한 액티비티 상태에서 작동하면서 세로 모드에서 가로 모드로 돌렸다가 다시 되돌리기를 여러 차례 반복합니다. 이처럼 기기를 돌리면 앱에서 Activity, Context 또는 View 객체 누수가 발생할 수 있는데, 이는 시스템에서 Activity를 다시 생성하는데 앱이 이러한 객체 중 하나에 대한 참조를 유지할 경우 이에 대한 가비지 수집을 수행할 수 없기 때문입니다.
  2. 다양한 액티비티 상태에서 자신의 앱과 다른 앱 사이를 전환합니다(Home 화면으로 이동한 후 다시 앱으로 돌아옴).

팁: monkey 테스트 프레임워크를 사용해 위 절차를 수행할 수도 있습니다. Monkey 테스트 프레임워크 실행에 대한 자세한 정보는 monkeyrunner 문서를 참조하세요.

This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a short survey?
Help us improve the Android developer experience.
(Sep 2017 survey)