[Android] Android Memory Leak 사례
Android를 개발하다 보면 무의식중에 Memory Leak을 유발하는 코드를 작성하게 될 가능성이 있다.
보통 가장 쉽게 Memory Leak을 유발하는 경우는 Activity
나 Android에 종속되는 컴포넌트들을 static 리소스로 관리하게 되는 경우이다. 하지만 실제로 이렇게 코딩하는 개발자는 거의 없으리라 생각된다.
그 외에 Memory Leak을 유발할 수 있는 케이스는 Inner Class
와 Anonymous Class
등을 사용하면서 발생할 수 있다. Inner Class
, Anonymous Class
등에 관련된 실제 코드상에서 관련될만한 클래스들은 Handler
, AsyncTask
, Thread
, Timer
등이 있다.
실제 Memory Leak을 유발하는 코드를 작성하기 전에 GC 동작에 대해서 이해가 필요하다. GC 알고리즘에 대해서는 별도로 포스팅을 해야 될 만큼 방대하기 때문에 여기서는 간략히 설명하겠다.
기본적으로 Java의 일반적인 객체는 strong reference
이다. 보통 GC 대상을 선정하는 과정에서 reachable
, unreachable
로 나뉘게 되는데 root set
으로부터 시작된 참조 사슬에 포함되어 있으면 reachable
객체, 참조 사슬에서 자신을 참조하고 있는 reachable
객체가 없을 경우 unreachable
객체로 나뉘게 된다.
물론 WeakReference
, SoftReference
, PhantomReference
등을 적용하면 GC 과정에서 사용자의 개입이 일어날 수 있지만 이 글에선 논외로 하겠다.
간단히 풀어서 이야기하면 자신을 참조하고 있는 객체가 있을 경우 GC 대상이 되지 않는다는 것이다.
아래는 대표적인 Android에서의 Handler
의 Memory Leak을 유발하는 코드이다.
위 코드는 onCreate
에서 handler
로 1분 뒤에 어떤 작업을 하도록 요청을 하고 그 아래에선 Activity
를 종료하도록 finish()
메서드를 수행한다. Java에서 non-static inner class
의 경우 outer class
에 대한 reference
를 가지게 된다. 위 코드에서 non-static inner class
로 선언된 Handler
는 LeakActivity
에 대한 reference
를 가지고 main thread에서 생성하였기 때문에 main thread의 Looper 및 Message Queue에 바인딩 된다.
LeakActivity
가 종료가 되었지만 Message Queue에 delay된 작업이 남아 있고 handler
에서는 LeakActivity
에 대한 reference
가 남아 있기 때문에 LeakActivity
는 GC의 대상이 되지 않는다.
아마 Android Studio에서 위와 같은 코드를 작성하면 Lint 메시지가 뜰 것이다.
그렇다면 어떻게 해야 Memory Leak이 발생하지 않을까? non-static inner class
는 outer class
에 대한 reference
를 가지지만 static inner class
는 outer class
에 대한 reference
를 가지지 않는다. 그리고 추가로 Handler
내부에서 Activity
의 메서드나 변수 등 리소스를 참조해야 될 경우 WeakReference
를 활용해야 한다.
위 코드를 보면 Runnable
도 Anonymous class
에서 static inner class
로 변경한 것도 눈여겨봐야 된다. Anonymous class
도 non-static inner class
와 동일하게 outer class
에 대한 reference
를 가지게 된다.
결론은 Inner Class
의 경우 Activity
의 Lifecycle에 동일하게 생성 및 종료가 보장된다면 non-static inner class
로 정의해도 되지만 그렇지 않은 경우 static inner class
로 정의해야 된다는 것이다.