클래스가 파일 핸들이나 네트워크 연결 같은 Unmanaged 리소스를 가지고 있는 경우, 이 리소스를 해제해 주기 위해 (1) Finalizer (Destructor), (2) Dispose 패턴을 사용할 수 있다.
(1) Finalizer는 Garbage Collector(GC)에 의해 객체 소멸 전에 호출되는 코드 블력으로 이 안에 파일 핸들을 닫거나 연결을 닫는 코드를 넣는다.
~MyClass() {
if (file != null) {
file.Close();
}
}
이 방식의 문제점은 실행이 GC에 의해 자동으로 결정되기 때문에 언제 이 코드가 실행될 지 모른다는 점이다. 즉, 객체가 다 사용되었는데, 파일은 계속 열려 있는 상태가 되고, GC가 언제 이를 Close할지도 모른다는 것이다. 이러한 문제점을 해결하기 위해 Dispose 패턴을 사용한다.
(2) Unmanaged 리소스가 있는 클래스는 일반적으로 IDisposable이라는 인터페이스를 추가하고, IDisposable.Dispose() 메서드를 구현한다.
public void Dispose() {
if (file != null) {
file.Close();
}
//클래스에 Finalizer가 있을 경우
GC.SuppressFinalize(this);
}
클래스 사용자는 이 Dispose()메서드를 실행하여 GC를 기다리지 않고 리소스를 즉시 해제한다. Dispose가 있더라도 클래스 사용자가 이를 호출하지 않았을 경우, 최후의 보루로 리소스를 해제하고 싶으면 개발자는 Finalizer를 추가할 수 있다. (물론 Finalizer를 추가하면 문제#24에서 소개한 부작용이 생길 수 있다)
[가산점] 위의 Dispose()메서드 안의 GC.SuppressFinalize를 왜 호출하는가?
[A] 클래스가 Finalizer 메서드와 Dispose메서드를 가지고 있을 때, Dispose() 메서드 호출한 후에는 Finalizer코드를 다시 실행할 필요가 없다. 즉, 리소스를 두번 해제할 필요가 없다. 이 경우 GC.SuppressFinalize를 호출하여 Finalization Queue안의 해당 객체에 플래그를 표시하여 Garbage Collection시 이 객체가 Freachable Queue로 이동하지 않게 한다. 따라서, Finalize()메서드가 호출되지 않게 하는 것이다.