본문
java.lang.ref #5. Finalize()와 *Reference의 사용예
VM은 어떤 객체가 사용되고 있는지, 그리고 어떤 객체가 더이상 도달불가능한지에 대해 지속적으로 모니터링하고, 도달불가능한 객체를 제거함으로서 객체가 점유하던 메모리 공간을 재사용할 수 있도록 한다. (이때 도달불가능한 객체를 'garbage'라고 하는데, 이는 프로그램상에서 더이상 사용되지 않고 그저 메모리 공간만 차지하기 때문이다) 대부분의 경우 GC는 뒤편에서 동작하고 있기 때문에 프로그램을 만들때에는 이에 대해 생각하지 않아도 된다. 하지만 GC가 언제 객체를 제거할지에 대해 알고자 하는등의 작업이 필요한 경우, 'finalizer'를 사용할 수 있다.
finalizer는 항상 finalize();로 규정되는 특별한 메소드이며, 여기에는 어떠한 인자도 필요하지 않는다. GC가 특정 객체에대해 unreachable이라고 판단하게되면, 객체내에 finalize()메소드가 있는지 확인한다. 만약 finalize()메소드가 존재하지 않는다면 GC는 이후에 객체를 메모리로부터 제거한다. 만약 객체에 finalize() 메소드가 존재한다면, GC는 해당 객체를 'Finalizable'하다고 표시해놓는다. 객체가 Finalizable하다고 표시되면, GC는 객체의 finalize()메소드를 이후에 호출할 수 있다. finalize() 메소드가 실행되고 끝마쳐지면, GC는 객체를 'Finalized'하다고 표시한다. 객체가 Finalized 되었다고 표시되면, GC는 객체의 도달가능성(reachability)에 대해 다시 조사한다. 이후 unreachable하다고 확인이 되면, GC는 객체를 메모리로부터 제거한다.
알아둘것은, GC는 객체에 finalize() 메소드가 있다면, 이 객체를 메모리로부터 제거하기 이전에, 도달가능성을 두번 확인해서, 도달불가능하다는것이 확인되어야 한다는 것이다. 도달가능성을 두번 확인하는 이유는 finalize() 메소드가 도달불가능한 객체를 도달가능하도록 만들 수도 있기 떄문이다(resurrection). 아래의 코드를 보자.
MyGlobals: object finalizedList = [];
class MyClass: object finalize() {
MyGlobals.finalizedList += self;
};
MyClass의 인스턴스가 도달불가능하게 되면, GC는 어느순간, 인스턴스의 finalize() 메소드를 호출하고, 이에따라 해당 인스턴스에 대한 참조를 MyGlobals.finalizedList에 추가한다. MyGlobals는 항상 도달가능하므로, MyGlobals.finalizedList가 가리키는 모든 참조 또한 도달가능하게된다. 이것은 즉, finalized된 인스턴스가 다시 도달가능하게 될 수 있다는 것이다. 따라서, 해당 참조가 MyGlobals.finalizedList에서 제거되지 않는 한 GC는 해당 객체를 제거할 수 없다.
finalized된 객체가 다시 도달가능하게 되더라도, GC는 객체의 finalizer를 단 한번만 부를 수 밖에 없다는 것을 기억해야 한다. 따라서 다음과 같은 상태의 변화를 생각해 볼 수있다. 객체는 처음에는 unfinalized의 상태로 존재한다 -> 해당 객체가 도달불가능하게됨을 GC가 알아채고, 따라서 finalizable하게된다 -> GC가 finalizer를 호출하게 되어 객체는 finalized 상태로 변한다 -> finalized되었으므로 GC는 객체가 도달불가능한지 다시 확인한 후 객체를 메모리로부터 지운다. GC는 finalizable한 객체에서만 finalizer를 수행할 수 있으며, finalizer가 수행되면 객체는 finalized된다 -> finalized에서는 finalizable 상태로 되돌아갈 수 없다.
GC의 구현방식중에 하나로, 동기화 추적방식이 있다. 이는 'root set'이라고 하는 부분으로부터 시작하여 모든 접근가능한 객체를 traverse하는 방식을 사용한다. 여기서 root set이란, 프로그램상에서 직접적으로 도달가능한 객체들의 집합으로서, 이들에는 지역변수와 소스코드에서 정의된 정적 객체들이 있다. GC는 모든 root set에있는 객체들을 reachable하다고 기록해놓으며, 그 후 이 객체들이 사용하는 각각의 객체들 또한 reachable하가도 하고 이는 계속 진행된다. 이 과정은 GC가 root set 객체들로부터 직접적/간접적으로 접근할 수 있는 객체들에 대해 모두 확인 할 떄까지 계속 이루어진다. 이 과정중에 확인되지 않은 객체들은 unreachable하다 하며, 따라서 삭제의 대상이 된다.
===========
원문출처(발췌) : Automatic Garbage Collection and Finalization
다른 일정으로 인해 java.lang.ref와 이와 관련한 뒷이야기에 대한 글은 이쯤에서 마무리 하려 합니다.(일주일동안 우려먹다니..) 대신 자료를 찾다가 여러 좋은 사이트들이 있길래 여기에 적어봅니다. 아래 4개의 링크에는 각각에 대한 소스와 설명이 매우 잘 나와 있습니다.
The Secret Life Of The Finalizer: page 1 of 2
Inside the Java Virtual Machine by Bill Venners
SofreReference, WeakReference, PhantomReference
Strong, Soft, Weak and Phantom References (Java)
WeakReference,SoftReference 和 PhatomReference 浅析
댓글