안드로이드 앱 개발자라면 누구나 한 번쯤은 마주치는 공포의 단어, ‘OutOfMemoryError(OOM)’. 사용자가 앱을 사용하던 중 갑자기 앱이 강제 종료되거나, 이유 없이 버벅거리는 현상을 겪는다면 그 뒤에는 ‘메모리’ 문제가 숨어있을 가능성이 높습니다.
이러한 문제를 해결하기 위한 첫걸음은 바로 내 앱이 메모리를 ‘어떻게’, 그리고 ‘얼마나’ 사용하는지 정확히 파악하는 것입니다. 안드로이드 시스템은 MEMINFO
라는 강력한 도구를 통해 특정 시점의 애플리케이션 메모리 사용량에 대한 아주 상세한 스냅샷을 제공합니다.
오늘은 이 MEMINFO
로그를 단순한 숫자 나열이 아닌, 내 앱의 건강 상태를 진단하는 ‘진단서’처럼 읽어내는 방법을 자세히 알아보겠습니다. 각 항목의 의미를 넘어, 어떤 지표를 중점적으로 봐야 하는지 실질적인 분석 팁까지 함께 공유해 드리겠습니다.
MEMINFO
란 무엇이며 어떻게 얻을 수 있나요?
MEMINFO
는 특정 프로세스(애플리케이션)의 메모리 사용 현황을 상세하게 보여주는 텍스트 보고서입니다. 주로 아래 두 가지 방법으로 얻을 수 있습니다.
- 실시간 확인 (
adb
사용): PC에 스마트폰을 연결하고 터미널에서 아래 명령어를 실행하면 현재 실행 중인 앱의MEMINFO
를 바로 확인할 수 있습니다. Shelladb shell dumpsys meminfo <패키지_이름_또는_PID>
- 버그 리포트(dumpstate) 분석: 사용자의 기기에서 ‘버그 리포트’를 추출하면, 해당 파일 내에 리포트 생성 시점의 모든 프로세스에 대한
MEMINFO
정보가 포함되어 있습니다. 현장에서 발생한 문제를 사후에 분석할 때 주로 사용됩니다.
이제 실제 MEMINFO
로그 예시를 보며 각 항목을 파헤쳐 보겠습니다.
** MEMINFO in pid 22565 [com.android.application.test] **
Pss Pss Shared Private Shared Private SwapPss Rss Heap Heap Heap
Total Clean Dirty Dirty Clean Clean Dirty Total Size Alloc Free
------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------
Native Heap 15956 0 2764 15948 8 0 1921 18720 61936 11078 46627
Dalvik Heap 11106 0 5836 9744 1164 1264 4 18008 27216 2640 24576
...
TOTAL 38302 3644 40428 28384 54540 4916 3253 128268 89152 13718 71203
... (Dalvik Details, App Summary, Objects 등) ...
숲을 먼저 보기: 가장 중요한 상위 지표들
복잡한 숫자들 속에서 길을 잃지 않으려면, 가장 먼저 숲 전체를 봐야 합니다. MEMINFO
보고서의 가장 마지막 App Summary
섹션의 TOTAL PSS
값이 바로 그 숲에 해당합니다.
항목 | 설명 |
---|---|
Pss Total | 프로세스가 사용하는 메모리의 비례적 총량입니다. 공유 메모리를 중복 계산하지 않고, 프로세스가 실제로 사용하는 메모리만 반영합니다. 이 값은 프로세스의 메모리 사용량을 정확히 파악하는 데 유용합니다. |
Pss Clean | 공유 메모리의 클린(Clean) 부분입니다. 클린 메모리는 디스크에 저장된 파일과 동일하며, 필요할 때 재사용할 수 있습니다. |
Shared Dirty | 공유 메모리의 더티(Dirty) 부분입니다. 더티 메모리는 디스크에 저장되지 않은 변경된 메모리로, 프로세스 간에 공유되지만 중복 계산됩니다. |
Private Dirty | 프로세스만 사용하는 더티 메모리입니다. 이 메모리는 프로세스가 독점적으로 사용하며, 디스크에 저장되지 않은 데이터를 포함합니다. |
Shared Clean | 공유 메모리의 클린(Clean) 부분입니다. 여러 프로세스가 공유할 수 있으며, 디스크에 저장된 파일과 동일합니다. |
Private Clean | 프로세스만 사용하는 클린 메모리입니다. 디스크에 저장된 파일과 동일하며, 필요할 때 재사용할 수 있습니다. |
SwapPss Dirty | 스왑된 메모리의 비례적 크기 중 더티(Dirty) 부분을 나타냅니다. 스왑은 물리 메모리 부족 시 디스크로 옮겨진 메모리를 의미하며, 더티 메모리는 변경된 데이터를 포함합니다. |
Rss (Resident Set Size) Total | 프로세스가 사용 중인 물리 메모리의 총량입니다. 공유 메모리가 중복 계산될 수 있으므로, 실제 물리 메모리 사용량을 파악하는 데 사용됩니다. |
Heap Size | 애플리케이션에 할당된 힙 메모리의 총 크기입니다. 이 값은 JVM 또는 네이티브 힙에서 관리되는 전체 메모리 공간을 나타냅니다. |
Heap Alloc | 실제로 사용된 힙 메모리입니다. 힙에서 현재 할당된 객체 또는 데이터의 크기를 나타냅니다. |
Heap Free | 사용 가능한 여유 힙 메모리입니다. 힙에서 아직 사용되지 않은 가용 메모리 공간을 나타냅니다. |
- Pss (Proportional Set Size): 한 프로세스가 사용하는 메모리 양을 가장 합리적으로 측정한 값입니다. 안드로이드에서는 여러 앱이 시스템 라이브러리 등 메모리 공간을 ‘공유’해서 사용하는데, Pss는 이 공유 메모리를 공유하는 프로세스 수로 나누어 공평하게 계산합니다. 따라서 여러 앱의 Pss를 모두 더하면 시스템의 전체 실제 메모리 사용량과 거의 근사해집니다.
- Key Point: 메모리 사용량을 평가할 때는
TOTAL PSS
값을 가장 중요한 기준으로 삼아야 합니다. 이 값이 내 앱의 실질적인 메모리 점유율입니다.
- Key Point: 메모리 사용량을 평가할 때는
- Rss (Resident Set Size): 해당 프로세스가 현재 사용 중인 물리 메모리(RAM)의 총량입니다. 공유 메모리가 중복으로 계산될 수 있어, 여러 프로세스의 RSS를 단순히 더하면 실제 물리 메모리보다 훨씬 큰 값이 나올 수 있습니다. Pss와 함께 참고용으로 활용합니다.
나무를 보기: 메모리는 어디에 사용되고 있을까? (App Summary 분석)
TOTAL PSS
로 전체적인 규모를 파악했다면, 이제 App Summary
섹션을 통해 그 메모리가 구체적으로 어디에 사용되고 있는지 살펴볼 차례입니다. 이 부분은 개발자가 이해하기 쉬운 카테고리로 메모리 사용 내역을 요약해 주어 문제의 원인을 파악하는 데 결정적인 단서를 제공합니다.
항목 | 설명 |
---|---|
Java Heap | 자바/코틀린 코드로 작성된 객체들이 저장되는 공간입니다. 대부분의 안드로이드 개발자가 주로 다루는 영역으로, 이 값이 비정상적으로 높다면 관리 코드 내에 메모리 누수(Memory Leak)가 있을 가능성이 큽니다. |
Native Heap | C/C++ 같은 네이티브 코드가 사용하는 메모리 공간입니다. 게임 엔진, 그래픽 라이브러리, 또는 JNI(Java Native Interface)를 통해 네이티브 코드를 사용하는 경우 이 수치가 높아집니다. 여기서 발생하는 누수는 추적하기가 더 까다로울 수 있습니다. |
Code | .dex , .so , .art 파일 등 앱의 실행 코드 자체가 차지하는 메모리입니다. 앱의 크기가 크거나, 많은 라이브러리를 사용하면 이 값이 커집니다. |
Stack | 각 스레드별로 할당된 스택 메모리입니다. 함수 호출, 지역 변수 등이 저장됩니다. |
Graphics | 그래픽 렌더링(UI, OpenGL 버퍼 등)과 관련된 메모리입니다. 복잡한 UI나 애니메이션이 많을수록 증가합니다. |
Private Other / System | 위의 카테고리에 속하지 않는 기타 메모리 및 시스템 관련 리소스 사용량입니다. |
“내 앱의 PSS가 비정상적으로 높은데, 그 원인이 Java Heap에 있을까, 아니면 Native Heap에 있을까?” 이 질문에 답하는 것이 메모리 분석의 첫 단추입니다.
더 깊은 단서 찾기: 세부 항목별 분석 팁
원인이 되는 메모리 영역을 특정했다면, 이제 더 깊은 단서를 찾아 나설 차례입니다.
Objects
– 메모리 누수의 흔적
보고서의 Objects
섹션은 현재 메모리에 살아있는 주요 안드로이드 컴포넌트 객체의 수를 보여주어 메모리 누수를 진단하는 데 매우 유용합니다.
항목 | 설명 |
---|---|
Views | 애플리케이션에서 생성된 UI 뷰(View) 객체의 수를 나타냅니다. 뷰의 수가 많다면, UI가 복잡하거나 메모리 누수가 발생하고 있을 수 있습니다. 뷰의 과도한 생성은 메모리 사용량을 증가시킬 수 있습니다. |
AppContexts | 애플리케이션 컨텍스트(AppContext) 객체의 수를 나타냅니다. 컨텍스트는 리소스, 설정 등에 접근하기 위해 사용됩니다. 컨텍스트의 수가 많다면, 불필요한 컨텍스트가 유지되고 있거나 메모리 누수가 발생하고 있을 수 있습니다. |
Assets | 애플리케이션에서 열린 자산(Asset) 객체의 수를 나타냅니다. 자산은 APK 내부에 포함된 파일(예: 이미지, 텍스트 파일 등)을 참조합니다. 자산 객체가 많다면, 불필요한 파일을 열었거나 메모리 누수가 발생하고 있을 수 있습니다. |
Local Binders | 로컬 바인더 객체의 수를 나타냅니다. 바인더는 프로세스 간 통신(IPC)을 위해 사용됩니다. 바인더 객체가 많다면, IPC가 과도하게 발생하거나 메모리 누수가 발생하고 있을 수 있습니다. |
Parcel memory | Parcel 객체가 사용 중인 메모리의 양을 나타냅니다. Parcel은 IPC 통신을 위한 데이터 컨테이너입니다. 이 값이 크다면, IPC 통신이 빈번하거나 메모리 누수가 발생하고 있을 수 있습니다. |
Death Recipients | Death Recipient는 Android 10 이후 제거된 개념입니다. 최신 버전에서는 이 항목이 표시되지 않습니다. 프로세스가 종료될 때 알림을 받는 객체의 수를 나타냈습니다. 이 값이 크다면, 프로세스 종료를 감시하는 객체가 많음을 의미했습니다. |
ViewRootImpl | ViewRootImpl 객체의 수를 나타냅니다. ViewRootImpl은 UI 트리의 루트를 관리하며, 화면 렌더링을 담당합니다. 이 값이 크다면, 여러 화면(Activity 또는 Dialog)이 동시에 열려 있거나 메모리 누수가 발생하고 있을 수 있습니다. |
Activities | 애플리케이션에서 생성된 액티비티(Activity) 객체의 수를 나타냅니다. 액티비티의 수가 많다면, 화면이 과도하게 생성되었거나 메모리 누수가 발생하고 있을 수 있습니다. |
AssetManagers | AssetManager 객체의 수를 나타냅니다. AssetManager는 애플리케이션의 자산(Asset)에 접근하기 위해 사용됩니다. 이 값이 크다면, 불필요한 AssetManager가 생성되었거나 메모리 누수가 발생하고 있을 수 있습니다. |
Proxy Binders | 프록시 바인더 객체의 수를 나타냅니다. 프록시 바인더는 원격 프로세스와 통신하기 위해 사용됩니다. 이 값이 크다면, 원격 IPC 통신이 빈번하거나 메모리 누수가 발생하고 있을 수 있습니다. |
Parcel count | Parcel 객체의 수를 나타냅니다. Parcel은 IPC 통신을 위한 데이터 컨테이너입니다. 이 값이 크다면, IPC 통신이 빈번하거나 메모리 누수가 발생하고 있을 수 있습니다. |
WebViews | 애플리케이션에서 생성된 WebView 객체의 수를 나타냅니다. WebView는 웹 콘텐츠를 표시하는 데 사용됩니다. WebView는 메모리를 많이 사용하므로, 이 값이 크다면 메모리 사용량이 크게 증가할 수 있습니다. |
- Views / ViewRootImpl / Activities / AppContexts: 이 값들은 각각 화면을 구성하는 뷰, 액티비티, 컨텍스트 등의 개수입니다. 사용자가 특정 화면을 닫고 다른 화면으로 이동했는데도 이 값들이 줄어들지 않고 계속해서 증가한다면, 이전에 사용했던 화면의 객체들이 메모리에서 해제되지 않고 남아있는 ‘메모리 누수’를 강력하게 의심해볼 수 있습니다.
Dalvik Details
– Java 힙 심층 분석
이 섹션은 Java Heap
을 더욱 세분화하여 보여줍니다. 예를 들어, .LOS(Large Object Space)
항목의 수치가 크다면 앱에서 비트맵 이미지나 대용량 배열 같은 큰 객체를 많이 사용하고 있다는 신호일 수 있습니다.
Native Allocations
– 네이티브 메모리 사용처
이 항목은 Native Heap
의 사용 내역을 더 자세히 보여줍니다. 특히 Bitmap (malloced)
항목을 통해 비트맵 이미지가 네이티브 메모리를 얼마나 사용하는지 파악할 수 있어, 이미지 관련 메모리 문제를 진단할 때 유용합니다.
마치며
MEMINFO
는 처음 보면 암호문처럼 보이지만, 그 구조와 각 항목의 의미를 이해하고 분석의 흐름을 잡으면 내 앱의 메모리 상태를 진단할 수 있는 가장 강력한 진단서가 됩니다.
앱의 성능 저하나 잦은 비정상 종료로 고민하고 있다면, 가장 먼저 dumpsys meminfo
를 통해 Total PSS 값을 확인하고, App Summary를 통해 Java Heap과 Native Heap 중 어디에 문제가 있는지 범위를 좁혀보세요. 그리고 Objects 섹션을 통해 액티비티나 뷰의 누수가 의심되는 정황은 없는지 확인하는 습관을 들인다면, 훨씬 더 안정적이고 쾌적한 고품질의 앱을 만드는 데 큰 도움이 될 것입니다.
읽어주셔서 감사합니다. 🙂
"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."