Efficient Android Threading #4 메모리 관리
이 글은 Efficient Android Threading 의 일부 내용만 발췌한 내용입니다.
자세한 내용은 책을 구입해서 보세용.
6.1. 가비지 컬렉션
-
각 프로세스(결과적으로 각 앱)는 자신만의 VM 과 가비지 컬렉터를 가진다.
-
진저브레드까지 안드로이드의 가비지 컬렉션은 메모리가 회수되는 동안 앱 실행이 중지되는 순차적 실행이었다.
이는 UI rendering 의 일시적 멈춤으로 이어질 수 있었다.
허니컴부터 GC 는 앱 스레드를 중지하지 않고 자체 스레드에서 동시적으로 실행된다.
-
달빅 GC 는 mark & swap 이라는 일반적인 두 단계 메커니즘을 사용한다.
마크 단계는 객체 트리를 탐색하고 다른 객체에 의해 참조되지 않는 모든 미사용 객체를 mark 한다.
사용하지 않는 객체는 가비지 컬렉션의 대상이 되고, 스왑 단계에서 mark 된 객체들의 할당이 해제된다.
6.2. 스레드 관련 메모리 누수
** 6.2.1. 스레드 실행
-
수명이 긴 스레드는 메모리 누수( 여기서는 오랫동안 메모리를 점유하고 있는 것도 누수의 일종으로 친다. ) 의 원인이 될 수 있다.
-
내부 클래스는 암시적으로 외부 클래스에 대한 참조를 가진다.
따라서 내부 클래스로 정의된 스레드는 스레드가 실행하는 동안 GC을 위해 마크되지 않는 외부 클래스에 대한 참조를 유지한다.
지역 클래스 및 익명 내부 클래스로 정의된 스레드 역시 실행되는 동안 외부 클래스를 GC 루트에서 도달 가능하게 유지한다.
-
정적(static) 내부 클래스는 외부 객체 자체가 아닌 외부 객체 클래스(Class<?>)에 대한 참조를 유지한다.
결국 instance 에 대한 참조가 해제되면 GC 대상이 된다.
-
대부분의 프로그래머는 테스크(Runnable)에서 실행 환경(Thread)를 분리하고 싶어 한다.
만약 새로운 Runnable 을 내부 클래스로 생성하면 생성된 Runnable 이 정적 내부 클래스에 의해 실행되는 경우에도 실행 중에 외부 클래스에 대한 참조를 유지한다.
이 case 를 혼동하지 않고 생각해야 한다.
-
안드로이드에서 누수에 대한 근본적인 이유는 구성요소, 객체, 스레드 사이의 생명주기가 불일치하기 때문이다.
-
Activity 의 onDestroy() 콜백은 다음과 같은 여러 가지 이유로 발생 할 수 있다.
사용자가 뒤로 가기 버튼을 누른다.
액티비티가 명시적으로 finish() 를 호출한다.
시스템이, 액티비티를 실행하는 앱 프로세스가 시스템 자원을 내주기 위해 종료될 수 있도록 결정했다.
설정 변경(configuration change)이 일어난다. 예를 들면 기기가 회전한 경우 재생성 할 때.
-
설정 변경과 사용자 탐색은 Activity 객체를 참조로 가진 많은 동시 스레드를 만들 수 있기 때문에 자동으로 시작된 스레드는 사용자가 시작한 스레드보다 더 높은 메모리 누수 위험을 가진다.
** 6.2.2. 스레드 통신
-
메시지는 메시지 큐에서 대기하거나 스레드에서 실행되는 동안 GC의 대상이 될 수 없다.
또한 메시지에 의해 모든 암시적 및 명시적 참조가 있는 핸들러와 Object, Runnable 역시 GC 대상이 아니다.
-
public class Outer{
Handler mHandler = new Handler(){
public void handleMessage(Message msg){
// 메시지 처리, 여기서 이미 Outer 를 참조한다.
}
};
public void doSend(){
Message msg = mHandler.obtainMessage();
msg.obj = new SampleObject();
mHandler.sendMessageDeplayed(msg, 60 * 1000);
}
}
위의 코드는 메시지가 handler 에 의해 처리될 때까지, SampleObject, Handler & Outer 에 대한 참조를 유지한다.
6.3. 메모리 누수 방지
-
메모리 누수를 방지하려면 다음의 방법들을 써야 한다.
1. static inner class 사용 : outer 를 참조할 필요가 없다면 이 방법이 좋다.
2. 약한 참조 사용 : outer 를 참조할 필요가 있을 때, outer 를 WeakReference 로 가지고 있으면 좋다.
3. 작업자 스레드 실행 중지
4. 작업자 스레드 유지 : 플랫폼에 따라 기존 액티비티에서 새로운 액티비티로 스레드를 이전시킬 수 있다.
5. 메시지 큐 정리 : removeCallbacks, removMessages 등을 통해 제거할 수 있다.
'프로그래밍 놀이터 > 안드로이드, Java' 카테고리의 다른 글
Efficient Android Threading #6 핸들러 스레드 : 고수준 큐 메커니즘 (0) | 2018.03.22 |
---|---|
Efficient Android Threading #5 기본 스레드의 생명주기 관리 (0) | 2018.03.21 |
Efficient Android Threading #3 프로세스 간 통신 (0) | 2018.03.19 |
Efficient Android Threading #2 스레드 통신 (0) | 2018.03.18 |
Efficient Android Threading #1 자바의 멀티스레딩,안드로이드 스레드 (0) | 2018.03.17 |
댓글