2004년 10월호 월간 마소에서 간단히 다루었던 Weak Reference 내용입니다. 원본 글 작성 이후로 Weak Reference는 크게 변경된 것이 없지만 내용을 다시 검토하고 보강 하였습니다.

Introduction to Weak Reference

닷넷에서 객체에 대한 참조는 강력한 참조(strong reference)와 약한 참조(weak reference)로 나누어 볼 수 있다. 강력한 참조는 일반적인 객체 참조로서 지금까지 여러분이 쭈욱 사용해왔던 참조를 말한다. 약한 참조는 System.WeakReference 클래스를 통한 참조로서 약한 참조만을 갖는 객체는 루트 참조로부터 참조 그래프 내에 포함되어 있더라도 가비지 컬렉션의 대상이 된다.  루트 참조와 참조 그래프에 대한 내용은 가비지 컬렉션에 대한 소개 글을 참고하기 바란다.

그림1은 약한 참조에 대한 개념을 잘 보여주고 있다. 그림1에서 obj1, obj2, obj3 변수에 의한 참조는 강한 참조이다. 반면 WeakReference 객체에 의한 참조는 약한 참조이다. 만약 그림과 같이 obj2 및 obj3 변수가 null 로 초기화 된다면 세 번째 MyObject 는 WeakReference 객체에 의해 여전히 참조되고 있지만 가비지 컬렉션이 수행되면 제거되는 객체가 된다.

image
그림1. Weak Reference 개념

약한 참조에 의해 참조되는 객체는 항상 그 객체가 실제로 유효한가 검사해야 한다. WeakReference 클래스의 IsAlive 속성은 약한 참조에 의해 참조되는 객체가 힙 상에 아직 살아 있는가를 반환한다. 객체가 살아 있다면 WeakReference 클래스의 Target 속성으로부터 강력한 참조를 얻은 후에 접근이 가능하다.

약한 참조의 주된 용도는 캐시이다. 언제 사용될지 모르는 (심지어 전혀 다시 사용되지 않을 수도 있는) 데이터 객체를 계속 힙 상에 상주 시키는 것 보다 약한 참조를 생성해 두는 것이다. 나중에 이 객체에 접근하고자 한다면 WeakReference.IsAlive를 통해 객체가 제거 되었는지 확인하고 만약 힙에 존재하지 않는다면 다시 생성하면 될 것이다. 다음 코드 조각은 전형적인 WeakReferece 클래스를 사용하는 용법을 보여주고 있다.

SomeType obj = new SomeType();
WeakReference ref = new WeakReference(obj);
// … 다른 로직 등등..
SomeType obj;
if (ref.IsAlive) {
obj = (SomeType)ref.Target;
}
else {
// 객체 다시 생성
obj = new SomeType();
}
// obj 변수를 통해 대상 액세스
 

다음 코드 조각은 약한 참조를 통해 세대별 가비지 컬렉션이 작동하는 것을 보여주는 예제이다. Obj2 객체가 생성되고 GC 0를 한번 수행함으로써 obj2 객체는 1 세대에 상주한다. 그리고 obj2 변수에 null을 할당함으로써 강력한 참조를 제거했다. 이제 obj2는 가비지 컬렉션의 대상이 된다. 하지만 GC 0 가 발생하면 obj2 객체는 여전히 힙에 남아 있게 된다. Obj2 는 GC 1 혹은 GC 2 가 발생한 다음에야 힙에서 제거된다.

object obj2 = new object(); 
Console.WriteLine("\nAllocation obj2 ..................................\n"); 
WeakReference wref = new WeakReference(obj2); 
GC.Collect(0); // obj2는 이제 Gen 1에 존재 
Console.WriteLine("GC 0 ---------------------------------------------"); 
obj2 = null; // root reference에서 제거 (obj2는 GC 대상임) 
Console.WriteLine("Is obj2 alive ? : {0}", wref.IsAlive); // true 
GC.Collect(0); // 다시 Gen 0 GC 
Console.WriteLine("GC 0 ---------------------------------------------"); 
Console.WriteLine("Is obj2 alive ? : {0}", wref.IsAlive); // true 
GC.Collect(1); // Gen 1 Collection 
Console.WriteLine("GC 1 ---------------------------------------------"); 
Console.WriteLine("Is obj2 alive ? : {0}", wref.IsAlive); // false

마치며…

약한 참조에 대해서는 이 정도만 다루어도 될 것 같다. 여기서 다룬 내용 외에도 약한 참조를 사용하면 Finalizer(곧 별도의 포스트에서 다루겠다) 에서 객체를 다시 부활(resurrection)시킬 수도 있다. 이러한 내용들은 MSDN에서 Weak References 항목을 참고하기 바란다.


경고 : 이 글을 무단으로 복제/스크랩하여 타 게시판, 블로그에 게시하는 것은 허용하지 않습니다.