본문 바로가기
C#

[C#] 소멸자 Finalize

by 대박플머 2014. 6. 13.

오늘 알아볼 녀석은 소멸자이다. 

C++을 사용해본 사람들은 소멸자를 잘 알것이다. 

메모리 관련해서 아주 중요한 영역이기 때문이다. 그런데 C#을 사용하는 사람들은 소멸자에 별로 관심이 없다. 

가비지 컬랙션이 있기 때문에 보통은 객체가 어떻게 되는지도 모르고 그냥 넘어가는 경우가 다반사이다. 


일반적으로 C#에는 가비지 컬랙터 기능이 있는 런타임을 대상으로 하지 않는 언어이기 때문에 개발시 메모리 관리가 필요하지 않다. 왜냐하면 .NET Framework 가비지 컬랙터에서 객체에 대한 메모리 할당과 해제를 암시적으로 관리하기 때문이다. 그러나 창, 파일 및 네트워크 연결처럼 관리되지 않는 리소스를 응용 프로그램에서 캡슐화할 경우, 소멸자를 사용하여 이 리소스를 해제해야 한다. 객체가 소멸 대상이 되면 가비지 컬랙터는 해당 객체의 Finalize 메서드를 실행합니다.

소멸자의 속성

1. 소멸자는 구조체에 정의할 수 없습니다. 이것은 클래스와만 사용할 수 있다.

2. 클래스는 하나의 소멸자만 가질 수 있다.

3. 소멸자는 상속되거나 오버로드될 수 없다.

4. 소멸자는 사용자가 호출할 수 없으며, 자동으로 호출된다.

5. 소멸자는 한정자를 사용하거나 매개 변수를 갖지 않는다.


사용법

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 
using System;
 
public class Program
{
    private static void Main()
    {
        Test t = new Test();
        Console.WriteLine("Main Call");
    }
 
}
 
public class Test
{
    ~Test()
    {
        Console.WriteLine("Test 소멸");
    }
}
 
/* 
Main Call
Test 소멸
*/

사용법은 아주 쉽다. 

소멸자는 객체의 기본 클래스에서 암시적으로 Finalize를 호출합니다. 

1
2
3
4
5
6
7
8
9
10
11
protected override void Finalize()
{
    try
    {
        // Cleanup statements...
    }
    finally
    {
        base.Finalize();
    }

finally에서 base.Finalize를 호출하는 것을 보면 알겠지만, Finalize 메서드는 마지막 파생 인스턴스부터 부모 인스턴스까지 상속 체인에 있는 모든 인스턴스에 대해 재귀적으로 호출 한다.


참고로 빈 소멸자는 사용하지 않는 것이 좋다. 클래스에 소멸자가 포함된 경우 Finalize 큐에 엔트리가 만들어진다. 그렇기 때문에 소멸자가 호출되면 큐 처리를 위해 가비지 수집기가 호출된다. 그래서 성능 저하를 가져온다.


소멸자가 호출되는 시기는 가비지 컬랙터에서 결정하므로 프로그래머는 이 시기를 제어할 수 없다. 

가비지 컬랙터는 응용 프로그램에서 더 이상 사용하지 않는 객체를 확인하여 소멸할 수 있는 것으로 판단된 경우 소멸자가 있으면 이를 호출하고 객체를 저장하는 데 사용된 메모리를 회수한다. 

소멸자는 프로그램을 종료할 때도 호출된다. Collect 를 호출하여 가비지 컬랱터를 강제로 실행할 수 있지만, 이렇게 하면 성능 문제가 발생할 수 있기 때문에 피하는 것이 좋다.



※리소스의 명시적 해제법

응용 프로그램에서 부담이 큰 외부 리소스를 사용하는 경우 가비지 컬랙터에서 객체를 정리하기 전에 개발자는 리소스를 명시적으로 해제 할 수 있다. 이렇게 하려면 객체에 정리를 수행하는 IDisposable 인터페이스의 Dispose 메서드를 구현 해야 한다. 이로 인해 응용 프로그램 성능이 대폭 향상될 수 있고 이러한 명시적 리소스 제어와 함께, 소멸자는 Dispose 메서드 호출이 실패할 경우 리소스를 정리하는 보호 수단이 되어야 한다. 

사용자 응용 프로그램에서 만들어지는 대부분의 객체에 대해 .NET Framework의 가비지 컬랙터를 사용하여 메모리 관리를 처리할 수 있다. 그러나 관리되지 않는 리소스를 포함하는 객체를 만든 경우에는 응용 프로그램에서 해당 객체의 사용을 마치면 이러한 리소스를 명시적으로 해제해야 한다. 가장 일반적인 형태의 관리되지 않는 리소스로는 파일, 창, 네트워크 연결 또는 데이터베이스 연결 등의 운영 체제 리소스를 래핑하는 객체를 들 수 있다. 가비지 컬랙터에서는 관리되지 않는 리소스를 캡슐화한 객체의 수명을 추적할 수는 있지만, 관리되지 않는 리소스를 해제하고 정리하는 방법에 대해서는 알 수 없다.


관리되지 않는 리소스를 정리 하기 위해서 가장 좋은 방법은 IDisplosable 인터페이스를 구현하는 것이다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
using System;
 
class BaseClass : IDisposable
{
   // Flag: Has Dispose already been called?
   bool disposed = false;
 
   // Public implementation of Dispose pattern callable by consumers.
   public void Dispose()
   { 
      Dispose(true);
      GC.SuppressFinalize(this);           
   }
 
   // Protected implementation of Dispose pattern.
   protected virtual void Dispose(bool disposing)
   {
      if (disposed)
         return
 
      if (disposing) {
         // Free any other managed objects here.
         //
         Console.WriteLine("disposing Call");
      }
 
      // Free any unmanaged objects here.
      //
      disposed = true;
   }
}
 
public class Program{
    public static void Main(){
        using(BaseClass _base = new BaseClass()){
            Console.WriteLine("Main Call");
        }
 
        BaseClass _base1 = new BaseClass();
        Console.WriteLine("Main Call");
        _base1.Dispose();
    }
}

돌려 보면 알겠지만 명시적으로 호출하거나 using문을 통해 관리되지 않는 리소스를 제어 할 수 있게 된다. 


이상  끝.....

'C#' 카테고리의 다른 글

[C#] 람다 표현식  (0) 2014.06.16
[C#] 내부 클래스  (1) 2014.06.16
[C#] Delegate 사용법  (0) 2014.06.12
[C#]명령 프롬프트에서 컴파일 하기 CSC.exe  (0) 2014.06.11
[C#] Boxing과 Unboxing의 고찰  (0) 2014.06.11