반응형
배경
- 개발을 해놓고 나니 메모리가 이미지를 로딩하는 등 앱을 지속적으로 실행시키면서 메모리가 슬금슬금 증가하는 모습을
안드로이드 프로파일러
를 통해서 발견했다. - 앱을 사용하면서 지속적으로 새로운 것들을 사용하기 때문에 메모리가 증가하는 것은 어떻게 보면 당연하지만, 문제는 사용하지 않는 것들에 대해선 적절하게
GC
가 수거해주어야 하고, 그에 따라 메모리가 줄어들고 난 뒤에 다시 늘어나는 경향을 보여야 하는데 메모리 사용량이 지속적으로 늘어나기만 하는 모습을 보였다. - 이는 어디에선가 사용하지 않는 객체를 해제하지 않아 메모리를 지속적으로 차지하고 있는,
메모리 누수
가 일어나고 있는 것으로 판단하였고 이를 개선하기 위해 메모리 누수에 대해 알아보고 개선하고자 하였다.
- 이러한 판단의 배경에는 저사양 기기로 테스트하는 경우에
OOM 에러
로 앱이 종료되는 현상이 있었고 해당 현상은 객체의 참조로 인해 화면 종료시에도 GC가 제대로 되지 않으면서 계속적으로 Activity 객체들이 쌓이고 있다고 판단했기 때문이다. Activity
의멤버 변수
가다른 Class
에 참조되어 메모리 해제가 되지 않으면, 그Activity
까지도 메모리가 완전히 해제가 되지 않아서, 사용하면 할수록 메모리 사용량의 증가가 비대해지는 영향을 끼친다.
- 이러한 판단의 배경에는 저사양 기기로 테스트하는 경우에
Memory Leak이란?
- 메모리 누수는,
Garbage Collector
가 객체에 메모리를 할당하지만(allocate
), 다시 해제(deallocate
)하지 않는 경우에 발생한다. - 즉,
Garbage Collector
객체 할당해제에 실패하여Application
에서 해당 객체를 더 이상 필요로 하지 않는 경우에도 객체를 계속해서 사용함에 따라 비효율적인 메모리 사용을 초래하는 것을 메모리 누수 라고 한다. - 이는
Garbage Collector
가 해당 객체는 여전히 필요한 객체라고 생각하기 때문인데, 이는 다른 객체로 부터 참조되고 있다고 판단하기 때문에 필요한 객체라고 판단하는 것이다. 그러나, 해당 참조는 Cleared되었어야 한다는 점에서 정상적인 판단이라 보기 어렵다. - 만약, 지속적으로 이러한 일이 일어난다면
Heap Memory
를 지속적으로 사용할 것이고 기기는 한정된 메모리를 가지고 있고, 앱에 할당할 수 있는 메모리가 제한되어 있기 때문에 더이상 메모리를 할당하지 못해, 결국엔 앱이 Crash 날 것이다. - 즉, 메모리 누수는
garbage collecting
될 대상에 참조가 포함된 객체가 있을 때 발생한다. 해당객체의 Instance
가 점점 더 많이 생성됨에 따라,이전 Instance
는 여전히 응용 프로그램의 메모리에 남아있게 된다. - 메모리에 오래 머물게 되면, 결국 Application에 할당된 메모리가 모두 소모되고 유저에 Application의 poor memory performance를 알리고 앱이 crash가 나게 되는 것이다.
Memory Leak을 탐지하는 방법
- 안드로이드 스튜디오에선 기본적으로
Profiler
를 통해 리소스의 사용량과 누수를 확인할 수 있고, Memory탭 에선 Leaks이 발생했음을 표시해주는 기능이 있기 떄문에 이를 통해 메모리 누수를 확인할 수 있다.- 시스템의 메모리에 앱이 어떤 영향을 주고 있는 지에 더 자세히 알아볼 수 있다는 장점이 있지만… 추적하기 쉽지않다는 것이 가장 큰 단점이다.
- 이외에 안드로이드에서 좀 더 편리하게 메모리 누수를 탐지하기 위한 방법으로
LeakCanary
를 사용할 수 있다. Memory Heap 파일
을Dump
뜬 뒤MAT
를 이용해 하나하나 인스턴스 개수를 찾아가며 하는 방법이 있다.
Android Studio Profiler
탐지 방법
- 앱 빌드후 앱이 실행되고 있는 상태에서,
Android Studio IDE
하단에 보이는Profiler 탭
을 열어Profiler
를 연다 - 해당 탭에서 + 버튼을 눌러 새로운 세션을 추가한다. 이 때, 앱이 실행되고 있는 기기를 선택한 뒤, 생성한 Application Package를 선택한다.
- 해당 탭을 열어 실행되고 있는 앱을 디버깅하기 시작하면, Memory 부분에서 현재 메모리가 어느정도 사용되고 있는지 실시간으로 보이게 된다.
Heap
의 종류default heap
When no heap is specified by the system.image heap
The system boot image, containing classes that are preloaded during boot time. Allocations here are guaranteed to never move or go away.zygote heap
The copy-on-write heap where an app process is forked from in the Android system.app heap
The primary heap on which your app allocates memory.JNI heap
The heap that shows where Java Native Interface (JNI) references are allocated and released.
- Memory 영역을 선택하게 되면, 메모리에 대한 자세한 내용을 확인할 수 있는데, 메모리 누수를 찾기위해서 휴지통 모양의 아이콘을 여러 번 클릭해
Force Garbage Collection
을 수행한다.- 이 동작후
GC
로도 해제되지 않은 메모리를 검출할 수 있다.
- 이 동작후
- 이후, capture heap dump를 선택후 Record를 클릭하면, Heap Dump와 함께 Leaks 항목을 통해 Memory Leak이 일어나고 있는 개수를 확인할 수 있다.
- 여기서 Leaks를 클릭하면, 메모리가 해제되지 않은 클래스를 보여준다.
- Leaks의 개수는 그 개수만큼 Memory Leak이 발생한 의미가 아닌, 반복 테스트한 횟수 만큼의 reference 숫자 만큼 보여지는 것이고, 만약 context가 해제되지 않고, 살아있다면 해당 reference까지 모두 잡히기 때문에 SupportRequestManagerFragment, ReportFragment까지 검출되게 된다.
- 중요한 것은 Application의 Class 중 1번째 항목부터 분석하는 것이다.
- 1번째 항목을 선택하고, 나오는 Instance List에서도 1번째 항목을 선택하면, Instance Detail 항목을 볼 수 있다. 여기서 References 탭을 클릭해, 1번째 항목을 선택하고 child로 타고 내려가다 보면, 의심스러운 변수명을 발견할 수 있다.
- 이제 여기서부터 발견해 나가는 것이 개발자의 역량이다. 1번째 reference child가 아니라면 다른 reference들을 찾아나가는 과정을 거치면서 어떤 원인으로 메모리 누수가 발생해냈는 지를 유추해 나가야 한다.
LeakCanary
탐지방법
- 내부적으로
LeakCanary
가 하는 것은, 단순히 destroy된 Activity를 모니터링 하며, 5초동안 기다린 뒤에, Garbage Collection을 강제하는 것이다.LeakCanary
는destroyed object
들의weak reference
들을 hold하기 위해objectWatcher
을 사용한다.AppWatcher
는 더이상 필요하지 않은object
들을watch
한다. 만약, 해당weak reference
들이 5초 내에 clear되지 않는 다면, watched instance들은 retain되는 것으로 간주하고, leaking instance의 가능성이 있음을 표시한다Application
이 실행되고 표시되는 동안ObjectWatcher
가 보유한Instance
retained object의 개수가 5개에 도달하거나 개발자의 조작을 통해,LeakCanary
는Java Heap
을 파일 시스템에 저장된.hprof
파일에Dump
한다. 그런 다음 Heap을 분석 보관된Instance
가Garbage collection
되지 않도록 하는 references chain을 확인한다.
- 위의 과정을 거치면서 만약
Activity
가 여전히 존재하는 경우,LeakCanary
는 Potential Leak으로 간주한다. - 이는 기본적으로
프로파일러
를 사용해 수동으로 수행하던Force Garbage-Collector
와Dump Heap
을 모니터링하고 강제로 실행했던Manual Process
의 자동화라고 볼 수 있다.LeakCanary
는Activity
,Fragment
,ViewModel
,Service
,Fragment’s View
를 모니터링한다.
Leak Sources
Leaks
는 2개의 카테고리에서 일어난다.App Level
Library Level
App Level
App level
Leak은 leak를 식별하고, 자체적으로 각 leak을 해결할 수 있다
Library Level
Library level
Leak은 할 수 있는 것이 많지 않다.- 따라서,
LeakCanary
에선 library leak을 자체적으로 표시해서 알려준다
- 따라서,
workable leaks
에 초점을 맞추거나,significant한 leak을 일으키는 library
에 대해 assist 할 수 있다는 점이LeakCanary
의 장점이다.
LeakCanary 사용법
- 단순히
build.gradle
파일에 아래를 추가해줌에 따라 사용 가능하다.LeakCanary
는 Debug에서만 사용되기 떄문에, debugImplementation으로 implementation 한다.
debugImplementation 'com.squareup.leakcanary:leakcanary-android:$version'
- 이후 Application을 실행하던 도중에,
Suspected leak
이 존재하면, 알림을 받을 수 있다.
출처: https://write.agrevolution.in/memory-leaks-in-android-apps-45a27c6ac35d - 해당 알림을 클릭하거나,
leak object
가 5개가 되었을 때 해당leak
과 관련해 analyze를 진행하게 된다. - 이후 analyze가 완료되고 나서, 다시 알림을 클릭하면 아래 이미지의 좌측과 같이
leak list
가 나오게 되고, 각각의 leak을 클릭하면 우측과 같이 어떠한 leak이 몇 번 발생헀는지 알려준다. 동시에 leak trace를 보여주게 된다.
- 최하단으로 내리면,
‘Leaking: YES’
와 동시에ObjectWatcher was watching this because com.example.myApplication.ListFragment received Fragment#onDestroyView() callback (references to its views should be cleared to prevent leaks
) 처럼 표시된 부분이 있다. leak trace
에서는leak이 발생할 수 없는 곳
은 빨간색의 밑줄이 그어지지 않고,발생 가능한 지점
부터 leak이 최초로 발생되었을 것으로 간주해 빨간색의 밑줄을 그어준다. 따라서, 해당 부분 부터 어디서 누수가 발생했을 지를 아래에서 부터 찾아가면 된다.Android Studio Profiler
보다는 조금 더 친절하지만, 여전히 메모리 누수의 원인을 찾아내는 것은 개발자의 몫이다. 따라서, 메모리 누수가 발생하는 흔한 원인을 먼저 제거하는 것이 중요하다.
반응형
'IT > Android' 카테고리의 다른 글
[Android/Refactoring] Memory Leak - 3. View's Reference는 Memory Leak의 대상일까? (0) | 2023.07.28 |
---|---|
[Android/Refactoring] Memory Leak - 2. 자주 발생하는 안드로이드 메모리 누수 방지하기 (0) | 2023.07.25 |
[Android] RecyclerView Deep Dive - 2. RecyclerView에서의 활용과 최적화 (0) | 2023.07.10 |
[Android] RecyclerView Deep Dive - 1. RecyclerView 정의와 동작원리 및 생명주기 (0) | 2023.07.05 |
[Android/FCM] (5) Android에서의 알림 수신 구현 (0) | 2023.03.11 |