태터데스크 관리자

도움말
닫기
적용하기   첫페이지 만들기

태터데스크 메시지

저장하였습니다.

'Reference'에 해당되는 글 1건

  1. 2007.09.06 Reference 객체는 어떻게 Garbage Collector와 대화하는가? (5)

Reference 객체는 어떻게 Garbage Collector와 대화하는가?

Enterprise Java 2007.09.06 16:36
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 상태이다.
Softly Reachable: SoftReference 객체를 통해서 접근되는 상태.
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 map = new HashMap();
ImageObject img = new ImageObject(userId + ".gif");
map.put(
userId, img);
...
ImageObject img = map.get(userId);
if(img == null) { // 아직 put 안되었음 }

- Soft Reference인 경우: 참조 대상 객체가 Collection 되었는지 여부를 확인해야 한다.
HashMap> map
= new HashMapSoftReference>();
ImageObject img = new ImageObject(userId + ".gif");
map.put(userId, new SoftReference(img));
...
SoftReference sr = map.get(userId);
if(sr == null) { // 아직 put 안되었음 }
ImageObject img = sr.get();
if(img == null) { // Oops!! Softly Reachable Object가 Collection 되었음...
img = new ImageObject(userId + ".gif");
sr = new SoftReference(img);
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만으로도 쉽게 원하는 효과를 얻을 수 있을 것이다.


신고
tags : , ,
Trackback 0 : Comments 5
  1. 맨땅헤딩 2007.09.07 02:22 신고 Modify/Delete Reply

    글 잘보고 있습니다. 근데 뭐 한가지 질문드려도 될가요. 오라클과는 상관없는데...
    저야 하는일이 그래서인지 코딩이나 어플리케이션쪽은 젬병이라서... 제가 돌리는 자바 프로그램하나가 while loop로 비디오의 모든 프레임을 읽어서 image processing을 처리하는게 있는데, 처음 한 5~10분은 제 속도가 나다가 어느순간 무지하게 느려집니다. 처음에는 gc로 어느정도 효과를 보지만 그나마도 시간이 지나면 다시 같은 현상이 나타납니다. 루프안에는 image processing 특성상 array를 좀 많이 사용합니다. 혹시 뭐 집히시는게 있는지...감사합니다.

  2. 맨땅헤딩 2007.09.07 02:26 신고 Modify/Delete Reply

    아.. 그리고 처리된 데이타를 keep track하는 memory-based video database를 가지고 있습니다. video database라고 해서 뭐 거창한거는 아니고, 그냥 extract된 feature values을 Java object으로 관리하는 겁니다. 컬러...기탕 등등. 그렇다고 해당 database크기가 커 봤쟈 1~200메가인데, 제가 사용하는 시스템이 메모리가 1G이거든요...

  3. 욱짜 2007.09.07 09:27 신고 Modify/Delete Reply

    JDK 종류와 버전, OS 정보, VM Option을 포함한 실행 Command, GC Dump(Option), Thread Dump(Option)를 메일로 보내주시면 시간되는 대로 집히는 바가 있는지 볼께요~ 참 그리고 말씀하신 Java Object를 메모리에 담을 때 어떤 자료구조를 사용하나요? java.util에 있는 Collection인지 아니면 자체 제작한 자료구조인지...

  4. 욱짜 2007.09.08 13:52 신고 Modify/Delete Reply

    메일 안주셔서 답글로 남깁니다.(주말이라서 그런가보네요 ^^) 다음 몇가지를 체크해보세요.
    1. Max Heap Size는 충분한가? (단, OS 차원에서 Paging in/out이 생기지 않을 정도로 크게)
    2. New Generation이 지나치게 크지 않은가? Object를 상주시키기 위한 Tenured Space를 크게 하기 위해 -XX:NewRatio=12 정도로 크게 주세요.
    3. 필요하면 Concurrent GC 사용. -XX:+UseConMarkSweepGC 옵션 사용해보세요.

  5. 맨땅헤딩 2007.09.11 23:13 신고 Modify/Delete Reply

    아..제가 독일 컨퍼런스 갔다온다음에 정신이 없어서 이제야 보내요. JDK는 1.4이고 windows xp에서 돕니다. gc dume, thread dump는 제가 잘 모르는거라... 하는일은 each image에 대해서 모든 픽셀 정보를 읽어서 [w][h][3] w:가로픽셀, h:세로픽셀, 3: Red, Green, Blue의 값을 넣는 것인데, 매 루프(이미지)할때 마다 initialize하고 다시쓰거든요. 필요에 따라 데이터를 DB에 저장하구요. 자료구조는 Graph structure를 사용하고 있고, 제가 직접 define한 object을 사용합니다. 그안에 잡다한 feature가 들어가죠. trajectory, color, position등등...주로는 vector를 씁니다.
    한번 말씀하신 옵션을 사용해 보도록 하겠습니다. 감사합니다.

Write a comment

티스토리 툴바