2. 다들 유니톤 재미있게 참가 하셨나요? 전… 노예코딩을 몇 일 하니까 피가 잘 돌아가는 느낌
이네요ㅎㅎㅎㅎ 여러분들 아무 말 없으신거 보니 프로젝트도 잘 진행되고 있을거라 생각합니다~!
다들 분발해서 이번 주 까지 프로토타입을 완성해서 제출해 주세요 캬캬캬캬캬캬캬캬
이번주에는 모두들 GC이 존재하는 프로그래밍 언어를 사용함으로써 메모리 누수로부터 안전하다
안 생각하시는 것 같아 재미있는 이슈를 가져왔습니다.
바로 GC를 이용해도 메모리 누수가 발생한다는 이슈입니다 ㅎㅎㅎㅎㅎㅎㅎ
3. Java에서의 OOM(Out Of Memory)는 정말 큰 이슈죠. 보통 안드로이드를 개발하는 개발자분은
이미지를 처리할 당시 한번쯔음 겪곤 합니다. 이 이슈는 서버를 개발할 때도 빗나갈 수 없는 이
슈중 하나죠. 그럼 여러분에게 메모리 누수가 발생하는 코드를 보여 드리겠습니다. 어디에서 메모
리 누수가 발생하는지 맞춰주세요~!
어디 부분에서 메모리 누수가 오는지 감이 오시나요?? 메모리 누수가 발생하는 곳은 new
NavigationDrawerActivity(this)와 new HattToast(this)에서 메모리누수가 발생합니다. 이런 메모리
누수는 왜 발생하고 이것을 방지하기 위해선 어떻게 해결할까요 ?
최초 Java1.1까지 OOM의 문제점을 해결하기 위하여 제공해주는 API가 없었습니다. 하지만 아래
에서 나오는 GC의 동작에서 좀 더 유연하게 객체를 처리하기 위하여 Java1.2 스펙에 Reference
API 시리즈가 추가되었습니다. 이 시리즈는 개발자가 직접 가비지 컬렉터(Garbage Collector)의 동
작에 간접적으로 개입할 수 있도록 제공합니다. Java의 Garbeage Collector는 동작방식이 매우 다
양하지만 공통적으로 2가지의 작업을 수행합니다.
1. Heap내의 객체 중 참조 카운터가 0인 가비지(garbage)를 찾아낸다.
2. 찾아낸 가비지의 메모리를 회수한다.
Java의 GC는 객체에 대해서 Reachable개념을 사용합니다. 인스턴스에 대해서 참조카운트가 0이
아니라면 Reachable 이며 참조카운트가 0이라면 unReachable입니다. 이 개념을 바탕으로 GC의
동작 원리를 한마디로 정리하면 참조 카운트가 0인 인스턴스 , 즉 unReachable에 대한 객체만 메
모리를 회수합니다. 이와 같이 개발자는 자신이 생성한 인스턴스가 어느 시점에 Reachable 상태
이고 언제 unReachable인지 알아야 합니다!
4. 위 그림에서 화살표는 각 객체의 Reachable로 표기했습니다. 연결이 되지 않은 객체들은
unReachable상태입니다. 또한 위 그림에서 보면 알 수 있듯이, 힙에 있는 객체들은 4가지중 하나
입니다.
1. 힙 속 객체가 자신을 참조하는 경우
2. JNI(Java Native interface)에 의해 생성된 객체에 대한 참조
3. Java 스택 , 즉 Java 메소드 실행 시 사용하는 지역 변수와 파라미터들의 의한 참조.
4. 메소드 영역의 static 변수의 대한 참조.
이 중 힙 속 객체가 자신을 참조하는 경우를 제외한 경우를 Root set이라 부르며 이에 Reachable ,
unReachable를 따질 수 있습니다.
5. Root set에서 Reachable과 unReachable한 경우들을 나타낸 그림입니다. 파란색은 Reachable한 경
우이고 파스텔톤의 빨간색은 unReachable한 객체입니다. unReachable은 다음 GC가 동작하면 메
모리에서 회수당할 객체입니다. 또한 unReachable한 객체가 Reachable한 객체를 참조하고 있어도
결국 unReachablee하다고 GC는 인식합니다. 하지만 Root set으로부터 어떠한 참조라 해도 연결이
되어있다면 Reachable상태가 됩니다.
본격적으로 Reference API시리즈를 알아봅시다. Reference API시리즈에는 Strong , Soft , Weak ,
Phantom Reference가 있습니다. 일반적으로 new Object()와 같이 new키워드를 이용하여 생성한
객체는 Strong Reference에 속해있으며 Reachable상태가 됩니다.
Strong Reference는 매우 강한 참조라는 의미로써 참조카운트를 증가시키는 주체가 됩니다.
개발자는 Strong Reference API를 사용하는 경우는 거의 없습니다. 주로 사용하는 API는 Weak
Reference , Soft Reference를 사용하죠. (Phantom Reference는 GC에게 정리당한 객체들을 참조하
는 API입니다.)
WeakReference는 참조 대상인 객체를 캡슐화(encapsulation)하여 객체를 생성합니다. 이걸 의사코
드와 그림으로 설명드리겠습니다.
WeakReference<Sample> obj = new WeakReference<Sample>(new Sample());
Sample ex = obj.get();
…….
ex = null;
6. 첫 줄에서 생성한 WeakReference객체는 Sample객체를 캡슐화 한 객체입니다. 참조된 Sample객
체에 접근하기 위해서는 get()메소드를 호출하여 사용해야 합니다.
이렇게 get메소드를 호출한 Sample객체는 ex객체에 StrongReference,WeakReference로 연결되어
있습니다. (이와 같이 하나의 객체는 여러 Reference에 참조될 수 있습니다.)
여기의 Sample 객체는 WeakReference에 캡슐화 하여 인스턴스를 생성했지만 이 상황에선
StrongReachable이라 부릅니다. 그 이유는 각각의 Reference는 숫자와 같이 높고 낮음이 있는
데요. 좀 더 강하게 연결되어 있는 Reference를 기준으로 Reachable을 판단하게 됩니다.
StrongReference > SoftReference > WeakReference > PhantomReference
Reference의 강도
이를 Strongly Reference, Softly Reference, Weakly Reference , Phantom Reference라 부르기도 하죠.
그렇다면! 다음 그림에 나오는 B객체는 어떤 Reference일까요~?
정답을 맞추셨기를 기원합니다!! B 객체는 Weakly Reference 면서도 Softly Reference 죠! 그렇기
때문에 SoftlyReference 라 부를 수 있습니다. Softly Reference 가 Weakly Reference 보다 강하기
때문이죠.
7. 지금까지 각각의 Reachable의 참조범위를 알아봤습니다. 각각의 Reachable이 언제 메모리에서 회
수되는 시점을 알아보고 갑시다.
new 키워드로 인스턴스를 생성하면 StronglyReference가 생성됩니다. 이렇게 생성된
StrongReference는 화면이 변경되거나 메소드가 종료되거나 쓰레드가 종료되는 시점에서 참조카
운트가 0이되서 메모리를 회수당합니다.
하지만 SoftlyReference와 WeaklyReference는 동작원리가 조금 신기합니다. SoftlyReference는 GC
가 아무리 동작해도 메모리를 회수당하지 않습니다. 하지만 여유로운 메모리가 존재하지 않으면
(이에 해당하는 수식이 존재합니다) SoftlyReference로 참조되어 있는 객체들은 메모리를 회수당하
죠.
strong reference 가 GC된 때로부터 지금 까지의 시간 > 옵션 설정 값 N(-XX:SoftRefLRUPolicyMSPerMB =
<N> ) *힙에 남아있는 메모리 크기
GC가 softly reference를 회수하는 순간
WeaklyReference는 그보다 더 허약합니다. WeaklyReference는 메모리가 여유가 있든 없든 GC가
동작하는 순간 메모리를 회수합니다.
이런 방법으로 WeakReference와 SoftReference를 이용하면 개발자가 GC가 동작하는 순간에 객체
의 운명을 정해줄 수 있으며 사용하지 않는 객체에 대해서 메모리회수가 일어납니다.