힙 덤프를 캡처하여 캡처 시점에 앱에서 메모리를 사용 중인 객체를 확인하고 메모리 누수 또는 끊김 현상, 멈춤, 심지어 앱 비정상 종료로 이어지는 메모리 할당 동작을 식별합니다. 특히 장시간의 사용자 세션 후, 더 이상 메모리에 없어야 한다고 생각되는데 아직 메모리에 있는 객체를 보여줄 수 있으므로 힙 덤프를 실행하는 것이 유용합니다.
이 페이지에서는 Android 스튜디오에서 제공하는 힙 덤프를 수집하고 분석하기 위한 도구를 설명합니다. 또는 명령줄에서 dumpsys
를 사용하여 앱 메모리를 검사할 수 있고 Logcat에서 가비지 컬렉션 (GC) 이벤트를 확인할 수도 있습니다.
앱 메모리를 프로파일링해야 하는 이유
Android는 관리되는 메모리 환경을 제공합니다. 즉, 앱에서 일부 객체를 더 이상 사용하지 않는다고 판단되면 가비지 컬렉터가 사용되지 않는 메모리를 힙에 돌려보냅니다. Android에서 사용되지 않는 메모리를 찾는 방법은 끊임없이 개선되고 있지만, 모든 Android 버전의 특정 시점에서 시스템이 코드를 일시중지해야 합니다. 대부분의 경우, 이러한 일시 중지는 감지할 수 없는 수준입니다. 하지만 앱이 시스템에서 수집할 수 있는 속도보다 빠르게 메모리를 할당하는 경우 컬렉터가 할당 요청을 충족시킬 만큼 충분한 메모리를 확보하는 동안 앱이 지연될 수 있습니다. 이로 인해 앱이 프레임을 건너뛰고 눈에 띄게 느려질 수 있습니다.
앱이 느려지지 않더라도 메모리 누수가 발생할 경우 앱이 백그라운드에 있는 동안에도 메모리를 유지할 수 있습니다. 이 동작으로 인해 불필요한 가비지 컬렉션 이벤트가 강제로 실행되어 시스템의 나머지 메모리 성능이 저하될 수 있습니다. 결국 시스템에서 메모리를 확보하기 위해 앱 프로세스를 강제 종료해야 합니다. 그러면 사용자가 앱으로 돌아올 때 앱 프로세스를 완전히 다시 시작해야 합니다.
앱의 메모리 사용을 줄일 수 있는 프로그래밍 방법에 관한 정보는 앱 메모리 관리를 참고하세요.
힙 덤프 개요
힙 덤프를 캡처하려면 메모리 사용량 분석 (힙 덤프) 작업을 선택합니다(프로파일러: 디버그 가능한 '앱' 실행 (데이터 완료) 사용). 그러면 힙 덤프가 캡처됩니다. 힙을 덤프하는 동안 자바 메모리의 양이 일시적으로 증가할 수도 있습니다. 힙 덤프가 앱과 동일한 프로세스에서 발생하고 데이터를 수집하려면 메모리가 필요하므로 이는 정상적인 현상입니다. 힙 덤프를 캡처하면 다음이 표시됩니다.
클래스 목록에는 다음 정보가 표시됩니다.
- Allocations: 힙의 할당 수.
Native Size: 이 객체 유형에서 사용하는 총 네이티브 메모리의 양 (바이트). Android에서
Bitmap
등 일부 프레임워크 클래스에 네이티브 메모리를 사용하므로 자바에서 할당된 일부 객체의 경우 여기에 메모리가 표시됩니다.Shallow Size: 이 객체 유형에서 사용하는 총 자바 메모리의 양 (바이트).
Retained Size: 이 클래스의 모든 인스턴스로 인해 유지되는 총 메모리 크기 (바이트).
힙 메뉴를 사용하여 특정 힙으로 필터링합니다.
- 앱 힙 (기본값): 앱에서 메모리를 할당하는 기본 힙입니다.
- Image heap: 부팅하는 동안 미리 로드된 클래스가 포함된 시스템 부팅 이미지입니다. 여기서는 할당이 결코 이동하거나 사라지지 않습니다.
- Zygote 힙: Android 시스템에서 앱 프로세스가 포크되는 COW(기록 중 복사) 힙.
정렬 드롭다운을 사용하여 할당을 정렬하는 방법을 선택합니다.
- Arrange by class(클래스별 정렬)(기본값): 클래스 이름을 기준으로 모든 할당을 그룹화합니다.
- Arrange by package: 패키지 이름을 기반으로 모든 할당을 분류합니다.
수업 드롭다운을 사용하여 수업 그룹으로 필터링합니다.
- 모든 클래스 (기본값): 라이브러리 및 종속 항목의 클래스를 포함한 모든 클래스를 표시합니다.
- Activity/Fragment Leaks 표시: 메모리 누수를 일으키는 클래스를 표시합니다.
- 프로젝트 클래스 표시: 프로젝트에서 정의한 클래스만 표시합니다.
클래스 이름을 클릭하여 인스턴스 창을 엽니다. 나열된 각 인스턴스에는 다음이 포함됩니다.
- Depth: 임의의 GC 루트에서 선택된 인스턴스까지 가장 짧은 홉 수.
- Native Size: 네이티브 메모리에서 이 인스턴스의 크기. 이 열은 Android 7.0 이상의 경우에만 표시됩니다.
- Shallow Size: 자바 메모리에서 이 인스턴스의 크기
- Retained Size: 도미네이터 트리에 따라 이 인스턴스가 지배하는 메모리의 크기
인스턴스를 클릭하여 필드 및 참조를 비롯한 인스턴스 세부정보를 표시합니다. 일반적인 필드 및 참조 유형은 Java의 구조화된 유형 , 배열 , 기본 데이터 유형 입니다. 필드 또는 참조를 마우스 오른쪽 버튼으로 클릭하여 소스 코드에서 연결된 인스턴스 또는 줄로 이동합니다.
- 필드: 이 인스턴스의 모든 필드를 표시합니다.
- 참조: 인스턴스 탭에서 강조 표시된 객체에 대한 모든 참조를 표시합니다.
메모리 누수 찾기
메모리 누수와 관련이 있을 수 있는 클래스로 빠르게 필터링하려면 클래스 드롭다운을 열고 Show activity/fragment leaks를 선택합니다. Android 스튜디오에서는 앱의 Activity
및 Fragment
인스턴스에서 메모리 누수가 의심되는 클래스를 표시합니다. 필터에 표시되는 데이터 유형은 다음과 같습니다.
- 삭제되었지만 아직 참조되고 있는
Activity
인스턴스 - 유효한
FragmentManager
가 없지만 아직 참조되고 있는Fragment
인스턴스
다음과 같은 상황에서는 필터에 거짓양성이 표시될 수 있습니다.
Fragment
를 생성하였지만 아직 사용하지 않은 경우Fragment
가 캐시되고 있지만FragmentTransaction
의 일부가 아닌 경우
메모리 누출을 더 수동으로 찾으려면 클래스 및 인스턴스 목록을 둘러보고 Retained Size가 큰 객체를 찾습니다. 다음과 같은 원인으로 인한 메모리 누수가 있는지 살펴보세요.
Activity
,Context
,View
,Drawable
및Activity
또는Context
컨테이너 참조를 보유할 수 있는 기타 객체에 대한 장기 참조Activity
인스턴스를 보유할 수 있는 비정적 내부 클래스(예:Runnable
)- 필요 이상으로 오랫동안 객체를 보유하는 캐시
잠재적인 메모리 누수가 발견되면 인스턴스 세부정보의 필드 및 참조 탭을 사용하여 관심 있는 인스턴스 또는 소스 코드 줄로 이동합니다.
테스트를 위한 메모리 누수 트리거
메모리 사용량을 분석하려면 앱 코드에 스트레스를 주어 강제로 메모리 누수를 유발해야 합니다. 앱에서 메모리 누수를 유발하는 한 가지 방법은 힙을 검사하기 전에 잠시 동안 앱이 실행되도록 하는 것입니다. 메모리가 힙의 할당 상한까지 서서히 누수될 수 있습니다. 하지만 누수가 적을수록 앱을 더 오래 실행해야 이러한 현상을 볼 수 있습니다.
다음 중 한 가지 방법으로 메모리 누수를 트리거할 수도 있습니다.
- 기기가 다양한 활동 상태에 있는 동안 기기를 세로 모드에서 가로 모드로 회전했다가 다시 되돌리기를 여러 번 반복합니다. 기기를 회전하면 앱에서
Activity
,Context
또는View
객체를 누출하는 경우가 많을 수 있습니다. 시스템에서Activity
를 다시 만들고, 앱에서 이러한 객체 중 하나의 참조를 다른 곳에 유지하는 경우 시스템에서 가비지 컬렉션할 수 없기 때문입니다. - 다양한 활동 상태에서 앱과 다른 앱 간에 전환합니다. 예를 들어 홈 화면으로 이동한 다음 앱으로 돌아갑니다.
힙 덤프 녹음 파일 내보내기 및 가져오기
프로파일러의 이전 녹음 파일 탭에서 힙 덤프 파일을 내보내고 가져올 수 있습니다. Android 스튜디오는 녹화 파일을 .hprof
파일로 저장합니다.
또는 jhat과 같은 다른 .hprof
파일 분석기를 사용하려면 .hprof
파일을 Android 형식에서 Java SE .hprof
파일 형식으로 변환해야 합니다. 파일 형식을 변환하려면 {android_sdk}/platform-tools/
디렉터리에 있는 hprof-conv
도구를 사용하세요. 원본 .hprof
파일 이름과 변환된 .hprof
파일을 작성할 위치(새 .hprof
파일 이름 포함)를 인수로 사용하여 hprof-conv
명령어를 실행합니다. 예를 들면 다음과 같습니다.
hprof-conv heap-original.hprof heap-converted.hprof