본문 바로가기
프로그래밍 놀이터/안드로이드, Java

[Android/안드로이드] Memory Leak ( 메모리 누수 ) 를 피하는 방법.

by 돼지왕 왕돼지 2012. 2. 23.
반응형


안녕하세요 돼지왕 왕돼지입니다.

오늘은
Memory Leak ( 메모리 누수 ) 를 피하는 방법. 이라는 주제로 이야기를 나누어보죠.


이 글은  
http://developer.android.com/resources/articles/avoiding-memory-leaks.html 의 번역글입니다.
( 직역이 아니라, 중요한 내용만 추리고, 의역을 더했습니다. ) 



안드로이드 앱은 16MB 의 Heap 까지만 사용할 수 있게 되어있다.( 적어도 G1 단말에서는 그렇다. ) 많은 양일수도, 적은 양일 수도 있다. 중요한 것은 신의 응용 프로그램이 다른 프로그램을 죽이면서 실행되면 안 된다는 것이다. 대부분의 memory leak 은 다음의 공통된 실수로부터 비롯된다. : Context 에 long-live reference 를 보관하는 경우이다.


안드로이드에서 Context 는 많은 일에 사용된다. 대부분은 resource 를 로드하거나 접속하기 위해서이다. 그래서 모든 widget ( view ) 들이 Context 를 파라미터로 받는 이유이다. 안드로이드 앱에서는 보통 Activity 또는 Application 을 통해 Context 를 접하게 된다. 그리고 대부분의 View 는 어플리케이션의 모든 리소스와 view hierarchy 에 접속할 수 있게 된다. 그래서 Context 에 memory leak 을 만든다면, ( 여기서 leak 이란 GC 대상이 되지 않는 것을 이름 ) 엄청난 메모리 손실을 초래할 수 있다. 조심하지 않는다면 이 memory leak 은 매우 쉽게 발생한다. 


기본적으로 단말의 orientation 이 변할 경우 system 은 activity 를 destory 하고 state 를 유지하면서 새롭게 activity 를 생성한다. 그 과정에서 안드로이드는 resource로부터 UI 를 재생성하게 된다. 자 이제 생각해보자. 당신이 앱에서 큰 사이즈의 비트맵을 생성한다면, 그리고 orientation 변경에 따라 매번 로드하고 싶지 않다면, 가장 쉬운 방법은 이 bitmap 을 static 으로 설정하는 것이다.

private static Drawable sBackground;
  
@Override
protected void onCreate(Bundle state) {
  super.onCreate(state);
  
  TextView label = new TextView(this);
  label.setText("Leaks are bad");
  
  if (sBackground == null) {
    sBackground = getDrawable(R.drawable.large_bitmap);
  }
  label.setBackgroundDrawable(sBackground);
  
  setContentView(label);
}



이 방법은 매우 빠르지만, 매우 잘못된 방법이기도 하다. 메모리 릭은 처음 orientation 이 변하는 순간부터 발생한다. 만약 Drawable이 view 에 붙어있다면, Drawable은 그 뷰를 callback 으로 들고 있는다. 이 TextView 는 Activity의 Context 를 reference 하고 있는다. 다시 말해 Drawable을 유지하는 것은 TextView 를 유지하는 것이며, 이는 Activity 의 Context 를 유지하기까지에 이른다. ( Reference 하고 있어 GC가 되지 않으므로.. ) 그리고 Activity 는 분명 많은 참조들을 가지고 있을 것이다. ( 소스 코드에 따라 다르겠지만, )


이 예제는 아주 간단한 Context 의 leak case 를 보여준다. Home Screen 의 source code 중 unbindDrawables() 함수를 살펴보면, 그 안에서 activity 가 destory 될 때 drawable 에 대한 reference 들을 null 로 처리해주는 것을 볼 수 있다. 흥미롭게도 이 Context 관련 memory leak 은 chain 으로 연결되어 추가적인 leak 을 발생시키고, 매우 나쁜 현상이다. 이 Chain memory leak 은 Out of memory error 를 급속도로 진전시킨다.


Context 관련 메모리 릭을 피하는 방법에는 기본적으로 2가지 방법이 있다. 가장 확실한 것은 자신의 scope 가 아닌 곳에서 사용하는 것을 피하는 것이다. Inner class나 다른 class 에 reference 를 유지하는 것도 매우 위험하다. 두번째 해결책은 Application context 를 사용하는 것이다. 이 context 는 application 이 살아있는 동안 쭉 유지된다. 그리고 activity life cycle 과도 상관이 없다. 만약에 context와 관련된 오래 유지되는 object 를 보존하고 싶다면 application object 를 사용할 것을 고려해라. Context.getApplicationContext()Activity.getApplication() 으로 쉽게 Context 를 구할 수 있다. 



정리하면, context 관련 leak을 피하기 위해서는 다음 사항들을 기억하자.

activity context 관련하여 오래 유지되는 reference 를 유지하지 말자. ( activity context 의 reference 생명주기는 반드시 activity 의 생명주기와 동일해야 한다. )

- Activit context 대신 Application context 를 사용하도록 하자.

- static 이 아닌, 생명주기가 관리되지 않는 inner class 에서의 context 관련 reference 관리를 피하자. 그리고, 
static inner class 에서 activity 를 weak reference 로 참조하라. ViewRoot 가 이렇게 하고 있다.
 
- Garbage Collector 는 메모리 릭에 대한 보험이 아니다.

 


로그인 없이 추천 가능합니다. 손가락 꾸욱~





반응형

댓글