posted by 방랑군 2009. 9. 30. 15:32

참조 : http://resisa.tistory.com/52

이번 포스트에서는 클래스의 생성자와 소멸자를 통해서 객체의 생명주기에 대해서 알아보고 난 후에 Spring.NET에서 IoC컨테이너에서 객체의 생명주기에 알아보겠습니다.

먼저 변수는 값 타입과 참조 타입이 있으며 값 타입에는 int, double, char 등이며 참조 타입은 class 등입니다. 값 타입은 스택이라는 영역의 메모리에 저장됩니다. 반면에 참조 타입은 new라는 연산자를 통해서 객체가 생성되면 멤버변수들은 힙이라는 영역의 메모리에 저장되며 이 객체 변수는 힙 영역에 메모리를 가리키는 주소값을 가지며 스택에 저장이 됩니다. 그래서 참조 타입이라고 불립니다. 그리고 .NET에서는 가비지 수집기가 존재하여 C++에서 처럼 delete를 해주지 않아도 시점을 알 수는 없지만 가비지 수집기가 힙 영역에 사용하지 않는 메모리를 정리를 해줍니다. 그렇다면 .NET에서는 메모리에 대해서 개발자는 신경쓰지 않아도 될까요? 정답은 상황에 따라서 다릅니다. 우리가 선언하는 변수를 리소스라고 생각할 때 리소스에는 두 가지가 존재합니다. 바로 관리되는 리소스와 네이티브 리소스입니다. 관리되는 리소스는 사용자가 직접 만든 클래스나 .NET Framework에서 제공하는 클래스입니다. 네이티브 리소스는 파일핸들이나 API를 사용하는 리소스입니다. 이 네이티브 리소스는 힙 외부에 할당된 메모리를 사용하기 때문에 가비지 수집기에서 메모리를 해제할 수 없습니다. 네이티브 리소스를 사용하는 프로그램에서는 이 부분에 대한 메모리를 관리해주어야 메모리 누수가 발생하지 않게 됩니다. 그러면 이러한 리소스들을 정리해주는 IDisposable 패턴에 대해서 알아보고 객체의 수명주기에 대해서도 알아보도록 하겠습니다.

public class DisposableClass : IDisposable

{

    private SqlConnection connection;

    private IntPtr fileHandle;

 

    public DisposableClass()

    {

        System.Diagnostics.Debug.WriteLine("생성자입니다.");

    }

 

    ~DisposableClass()

    {

        Dispose(false);

        System.Diagnostics.Debug.WriteLine("소멸자입니다.");

    }

 

    [System.Runtime.InteropServices.DllImport("Kernel32")]

    private extern static Boolean CloseHandle(IntPtr handle);

 

    public void Dispose()

    {

        Dispose(true);

        GC.SuppressFinalize(this);

 

        System.Diagnostics.Debug.WriteLine("Dispose() 호출.");

    }

 

    protected virtual void Dispose(bool disposing)

    {

        if (disposing)

        {

            if (connection != null)

                connection.Dispose();

        }

 

        if (fileHandle != IntPtr.Zero)

        {

            CloseHandle(fileHandle);

            fileHandle = IntPtr.Zero;

        }

    }

 

}

=> DisposableClass 클래스는 관리되는 리소스인 SqlConnection과 네이티브 리소스인 IntPtr를 멤버변수로 가지고 있습니다. 또한 IDisposable 상속받아 구현하고 있는 것을 볼 수 있습니다. 여기서 IDisposable 패턴에 대해서는 자세히 설명하지 않겠지만 Dispose 메소드에서는 Dispose메소드가 호출이 되면 소멸자를 호출할 필요가 없다는 것을 가비지 수집기 에 알려주기 위한 부분(GC.SuppressFinalize(this))이 있으며 매개변수를 가지고 있는 Dispose 메소드가 있어 매개변수가 true일 때는 관리되는 리소스와 네이티브 리소스를 모두 정리하는 것을 알 수 있지만 false일 때(소멸자에서)는 네이티브 리소스만을 정리하는 것을 볼 수 있습니다. IDisposable 패턴에 대한 자세한 사항은 아래의 사이트를 참고 하세요.
http://msdn.microsoft.com/ko-kr/magazine/cc163392.aspx

그러면 이제 DisposableClass 클래스를 생성하도록 해보겠습니다.
 1번 : using 키워드 미사용
DisposableClass disposableClass = new DisposableClass();
 2번 : using 키워드 사용
using (DisposableClass disposableClass = new DisposableClass())

{

      

}

=> 2번처럼 using키워드를 사용하면 자동으로 Dispose메소드를 호출해주어 리소스를 해제시켜주는 것을 알 수 있습니다. 이렇게 using 키워드를 사용하기 위해서는 당연히 IDisposable
상속받아 구현해주어야 합니다.

여기서 저는 한가지 궁금증이 생겼습니다. SqlConnection과 같이 관리되는 리소스를 using키워드와 함께 객체로 생성하면 변수 connection의 값도 모두 해제되는 것인줄 알았습니다. 하지만 Dispose메소드는 변수 connection의 멤버변수들(리소스)을 해제해주는 것이지 connection의 값(참조주소)이 해제되는 것은 아닙니다. 물론 이 참조값은 int형의 4바이트뿐이 안됩니다. 또한 이 값은 가비지 수집기에 의해서 해제가 됩니다. 가비지 수집기는 어떻게 이 참조값을 해제해주는지 알 수 없지만 가비지 수집기에 명시적으로 이 값을 해제시키는 방법은 있습니다. 바로 connection변수에 null를 넣어주는 것입니다. GC.Collect()란 메소드는 명시적으로 가비지 수집기에게 메모리를 정리하라는 것을 알려줍니다. 만약에 connection변수에 null이란 값을 넣어주고 GC.Collect()를 호출하면 connection변수의 참조값을 바로 해제해줍니다. 실제적으로 코드가 생성되는 것을 한번 살펴보아야 확실히 알 수 있겠지만 제 생각으로는 uisng키워드에서 생성된 객체는 {}에서만 사용할 수 있으며 다른 곳에서는 사용할 수 없는 값이기 때문에 아마 {}안에서 마지막에 null를 대입해주는 것이 아닐까라는 생각이 듭니다.