----------------------------------------------------------------------------------
출처: http://ukja.tistory.com/50
----------------------------------------------------------------------------------
Reference와 Garbage Collector
앞서 블로그(http://blog.naver.com/ukja/120042116328)에서 Java에서 대용량 데이터를 다룰려면 Reference를 사용할 수 있어야 한다고 언급한 바 있다.
간단한 테스트를 통해 Reference를 사용했을 때 Garbage Collection에서 어떤 차이를 유발하는지
살펴보자.
Reference와 Reachability
JDK 1.2부터 Reference 객체가 제공되며 java.lang.ref 패키지에 필요한 클래스들이 정의되어 있다. Reference 객체는 Application과 Garbage Collector가 서로 대화를 주고받을 수 있는 유일 무이한 객체라는데 그 의미가 있다.
일반적으로 Java에서 객체를 참조하는 방식은 다음과 같다.
SomeObject obj = new SomeObject();
...
위에서 obj 변수는 SomeObject 객체를 참조하고 있다. Garbage Collector는 이 객체가 Reachable한지 Unreachable 한 지에 따라 Collection 여부를 결정한다. 아래 두 경우를 보자.
a) Unreachable 객체
for(...) {
SomeObject obj = new SomeObject();
...
}
b) Reachable 객체
SomeObject obj;
for(...) {
obj = new SomeObject();
...
}
a)의 경우 obj는 for() {} 구문이 끝나고 나면 Unreachable 상태가 된다. 따라서 다음 번 GC에서 Collection 대상이 된다. 반면 b)의 경우 obj는 for() {} 구문이 끝나더라도 여전히 Reachable 상태이다. 따라서 Collection 대상에서 제외된다.
JDK 1.2에서 Reference 객체가 소개되기 전까지는 객체의 상태는 Reachable 또는 Unreachable 둘 중의 하나여야만 했다. 하지만 Reference 객체의 등장으로 보다 다양한 상태를 정의할 수 있게 되었다. Java 객체의 상태는 이제 다음과 같이 다섯개로 나누어진다.
Strongly Reachable: Reference 객체를 사용하지 않으면서 Reachable한 상태.
즉, 우리가 사용하는 대부분의 Reachable 객체는 Strongly Reachable 상태이다.
Weakly Reachable: WeakReference 객체를 통해서 접근되는 상태
Phantomly Reachable: PhantomReference 객체를 통해서 접근되는 상태(본 블로그에서는 다루지 않음)
Strongly Reachable 상태는 우리가 평소에 객체를 액세스하는 방식을 말한다.
Softly Reachable 상태와 Weakly Reachable 상태는 Reachablility를 더 세분화한다.
한마디로 부드럽게 참조하는 방법과 약하게 참조하는 방법이 추가된 것이다.
Strongly/Softly/Weakly Reachable의 상태 차이는 Garbage Collector가 객체를 어떻게 청소하느냐에 따라 구분된다. Strongly Reachable 상태의 객체는 절대 Collection이 되지 않는다. Garbage가 아니기 때문이다. 반면 Softly Reachable 상태의 객체는 Memory Pressure(주로 Full GC)가 발생할 때 청소가 가능하다. Weekly Reachable 상태의 객체는 Softly Reachable 상태보다 더 약해서 GC가 발생할 때마다 적극적으로 Collection이 된다.
Softly/Weakly Reachable 상태의 객체는 분명히 Garbage가 아니면서도 메모리 요구량이 높아지는 시점에는 마치 Garbage 처럼 처리된다는 속성을 지니고 있다. 이런 의미에서 Reference 객체를 Application과 Garbage Collector가 서로 통신하는 유일한 방식으로 설명하기도 한다.
항상 참조(Reference)는 하고 있지만, 참조 대상이 반드시 메모리에 보관하고 있을 필요가 없는 객체들을 정의하고 싶다면? 바로 SoftReference나 WeakReference를 사용하면 된다. 예를 들어 사용자가 어떤 이미지를 요구할 때마다 메모리에 올려서 내용을 보여주고, 메모리에 올라온 이미지는 Cache에 담아서 재활용한다고 가정해보자. 사용자가 늘어남에 따라 메모리 요구량이 점점 늘어나서 더 이상은 이미지를 Cache에 담을 수 없게 될 것이다. 이런 경우를 해결하려면 LRU 알고리즘과 같은 기법을 써서 복잡한 자료 구조를 구현해야 한다. 하지만 SoftReference를 쓰면 매우 간단하게 해결된다. 즉 메모리에 올라온 이미지 객체를 직접 참조하지 않고 SoftReference를 통해서 참조하는 것이다. 이렇게 되면 Memory Pressure가 발생할 때 자연스럽게 Collection이 이루어지게 된다.
SoftReference를 사용하는 간단한 예제
SoftReference/WeakReference를 사용할 경우에는 Reference가 참조하고 있는 객체가 언제든지 Collection될 수 있다는 전제하에서 코딩을 해야 한다. 아래에 간단한 예제가 있다.
(Java 5의 Template 기능을 사용했음...)
- Strong Reference인 경우: 그냥 쓰면 된다
HashMap
ImageObject img = new ImageObject(userId + ".gif");
map.put(
userId, img);
...
ImageObject img = map.get(userId);
if(img == null) { // 아직 put 안되었음 }
- Soft Reference인 경우: 참조 대상 객체가 Collection 되었는지 여부를 확인해야 한다.
HashMap
ImageObject img = new ImageObject(userId + ".gif");
map.put(userId, new SoftReference
...
SoftReference
if(sr == null) { // 아직 put 안되었음 }
ImageObject img = sr.get();
if(img == null) { // Oops!! Softly Reachable Object가 Collection 되었음...
img = new ImageObject(userId + ".gif");
sr = new SoftReference
map.put(userId, sr);
}
...
Strong Reference/SoftReference/WeakReference의 행동 방식에 대한 간단한 테스트
아래는 Strong Reference/SoftReference/WeakReference가 GC에 어떤 영향을 미치는지 간단하게 테스트한 결과이다
- Strong Reference/SoftReference/WeakReference를 이용해 대량의 객체를 생성한다
- 그 후 대량의 메모리를 할당한다(Case1 = 8M, Case2 = 10M)
- 참조 대상 객체가 Collection되는지를 확인한다.
(소스는 첨부 파일 참조)
1. Object 생성 후 8M를 요구할 때
- Strong Reference
500000 accomplished
Total Application Pause Time = 0.319163
Full GC: Pause Time = 0.000000, Count = 0, Average=0.000000
Minor GC: Pause Time = 0.319163, Count = 4, Average=0.079791
- SoftReference: 아직 참조 대상이 Collection 되지 않았음(Full GC가 생기지 않았음에 유의)
Null = 0, Not Null = 500000 <-- 전부 Not Null
500000 accomplished
Total Application Pause Time = 0.531873
Full GC: Pause Time = 0.000000, Count = 0, Average=0.000000
Minor GC: Pause Time = 0.531873, Count = 6, Average=0.088645
- WeakReference: 일부 참조 대상이 Collection 되었음(Full GC가 없음에도 불구)
Null = 21224, Not Null = 478776 <-- 일부 Null
500000 accomplished
Total Application Pause Time = 0.514273
Full GC: Pause Time = 0.000000, Count = 0, Average=0.000000
Minor GC: Pause Time = 0.514273, Count = 6, Average=0.085712
2. Object 생성 후 10M를 요구할 때
- Strong Reference
500000 accomplished
Total Application Pause Time = 0.545487
Full GC: Pause Time = 0.226339, Count = 1, Average=0.226339
Minor GC: Pause Time = 0.319148, Count = 4, Average=0.079787
- SoftReference: 참조 대상 전체가 Collection 되었음(Full GC가 생겼음)
Null = 500000, Not Null = 0 <-- 전부 Null
500000 accomplished
Total Application Pause Time = 1.521140
Full GC: Pause Time = 1.027627, Count = 2, Average=0.513814
Minor GC: Pause Time = 0.493513, Count = 5, Average=0.098703
- WeakReference: 참조 대상 전체가 Collection 되었음(Full GC가 생겼음)
Null = 500000, Not Null = 0 <-- 전부 Null
500000 accomplished
Total Application Pause Time = 0.980496
Full GC: Pause Time = 0.506724, Count = 1, Average=0.506724
Minor GC: Pause Time = 0.473772, Count = 5, Average=0.094754
위의 결과를 보면 SoftReference와 WeakReference를 사용할 경우 Memory Pressurce가 있을 때 참조 대상 객체가 Collection되는 것을 확인할 수 있다. 따라서 메모리 사용이 좀 더 효율적이 된다.
단 Reference 객체 자체가 메모리를 차지하기 때문에 오버헤드가 있을 수 있다. 하지만 이런 오버헤드는 메모리의 효율적인 사용에 의해 충분히 상쇄된다.
------------(참조)----------------------------------------------------------------
java.util.WeakHashMap은 WeakReference를 이용하는 HashMap 이다.
만일 중요하지 않은 객체를 메모리에 올리면서 메모리를 효율적으로 사용하기 원한다면 WeakHashMap을 사용해 볼 것을 권장한다.
---------------------------------------------------------------------------------
정말 진지한 Application이라면...
비록 SoftReference/WeakReference가 메모리를 효율적으로 사용하는 매우 편리한 방법을
제공하지만 역시 진지한 Application에서 채용할 만큼 효율적인지는 의문이다.
정말 지능적인 Memory Cache를 구현하려면 LRU에 기반한 복잡한 자료구조를 구현할 수밖에 없을 것이다. Garbage Collector의 행동 양식은 Application 입장에서는 예측이 불가능하기
때문에(즉 자주 사용되지 않는 객체를 언제 메모리에서 내릴 것인가...) 신뢰할 만한 기능을
구현하기 어렵다.
반면 좀 덜 진지한 Application이라면 SoftReference나 WeakReference만으로도 쉽게 원하는 효과를 얻을 수 있을 것이다.
'IT_Programming > Java' 카테고리의 다른 글
[펌] Java theory and practice: 메모리 누수와 약한 참조 (0) | 2010.06.23 |
---|---|
[펌] Java Reference Object의 이해와 활용 (0) | 2010.06.23 |
[펌] Java에서의 Out Of Memory Error(OOME)에 대한 정리... (0) | 2010.06.23 |
Java로 구현한 JavaScript의 escape()와 unescape() (0) | 2010.06.17 |
XML Parsing 관련 예제 코드 (0) | 2010.05.04 |