이 글은 www.androiddesignpatterns.com에 소개된 글을 저자의 동의를 얻고 번역한 것임을 알려드립니다.

원문 : http://www.androiddesignpatterns.com/2013/04/activitys-threads-memory-leaks.html

 

안드로이드 프로그래밍에 있어서 어려운 점 한가지는 Activity의 Lifecycle에 종속적이지 않는 Long-running tasks를 구현하는 것과 이때 발생하기 쉬운 Memory leaks을 방지하는 것이다.  Activity의 생성과 동시에 새로운 Thread를 생성하는 아래의 코드를 살펴보자.

Configuration이 변경되어 Activity가 소멸(destroy)되었다가 다시 생성(create)될 때, Android System이 Activity에서 사용한 메모리를 회수했다가 다시 할당한다고 간주하기 쉽다.  하지만 위의 코드에서는 메모리 릭이 발생함은 물론 발생한 메모리 릭으로 인해 성능에 상당한 영향을 미치게 될 확률이 높다.

 

How to Leak an Activity.

이전 글을 읽었다면 메모리릭이 어디서 발생하는지 확실하게 알 수 있다. 자바에서는 non-static anonymous class의 경우 자신의 outer class에 대한 implicit reference를 갖게 되고 이로인해 GC(Garbage Collector)가 garbage collection을 할 수 없게 만든다.

위의 코드에서는 Thread가 outer class인 MainActivity에 대한 implicit reference를 갖게되어 MainActivity가 garbage collect될 수 없도록 만든다.

 

Activity object는 전체 View Hierarchy와 Resource들에 대한 reference를 갖고 있기 때문에 Activity object가 메모리 릭의 대상이 되면 상당한 양의 메모리 릭이 발생했다고 생각해야 한다.

Configuration의 변경으로 인해 발생하는 문제는 Configuration이 변경될 때마다 누적되어 문제를 악화시킨다. 예를 들어 10번의 Orientation 변경은 10개의 Activity Object에 대해 메모리 릭을 발생시키는 것을 Eclipse Memory Analyzer를 통해 확인할 수 있다.

Configuration의 변경이 일어날 때마다 Android System은 새로운 Activity를 생성하고 이전에 생성한 Activity는 garbage collect 될 수 있도록 남겨놓는다. 하지만 Activity 내에 thread가 Activity에 대한 implicit reference를 갖고 있기 때문에 Activity는 garbage collect되지 않고 memory leak을 발생시킨다.

위에서 언급했듯 Activity object는 전체 View Hierarchy와 Resource들에 대한 reference를 갖고 있기 때문에 Activity에 대해 memory leak이 발생하게 되면 이와 관련된 모든 resource들에 대해 memory leak이 발생하게 된다.

위와 같이 thread를 static으로 선언하면 더이상 outer class에 대한 implicit reference를 갖지 않게 되어 outer class인 Activity가 정상적으로 garbage collect될 수 있다.

 

How to Leak an Thread.

Activity가 새로 생성될 때마다 발생하는 2번째 이슈는, Thread에 대한 메모리 릭이다. Java에서 Thread는 GC roots다. 즉,  Android Application의 Dalvik Virtual Machine(DVM)은 thread가 garbage collect되지 않고 실행될 수 잇도록 모든 active thread에 대한 hard reference를 저장한다. 이 때문에 thread를 사용할 때는 cancellation policy가 반드시 존재해야 한다.

위의 코드에서는 Activity가 종료될 때 thread의 작업을 종료하는 과정을 추가함으로써 thread에 대한 memory leak을 방지할 수 있다.

만약, Configuration 변경시에도 동일한 thread를 유지하고 싶다면 UI-less Worker Fragment를 통해 long-running task가 가능하도록 고려해보자. (Check out this post)

 

Conclusion.

안드로이드에서 Activity의 Lifecycle에 종속적이지 않는 Long-running tasks를 구현하는 것은 쉽지 않고, 주의를 기울이지 않으면 memory leak이 발생하기 쉽다. Activity와 관련된 long-running background task를 수행하는 Thread/AsyncTask를 설계할 때 참고해야 할 일반적인 Tip들은 아래와 같다.

 

  • non-static보단 static inner class를 사용하자. non-static inner class는 outer Activity class에 대한 reference를 갖고 이 때문에 Activity class에 대한 memory leak이 발생할 수 있다.  만약 static inner class가 Activity에 대한 reference가 필요할 경우 WeakReference로 갖도록 하자.
  • Java는 running thread를 대신 처리(종료)해주지 않는다. Java thread는 명시적으로 종료하거나 작업을 끝냈거나 process가 종료되기 전까지는 사라지지 않는다. 따라서 cancellation policy를 구현하는 것은 매우 중요하다.

Leave a comment If you have any questions.