Using 키워드와 IDisposable

C#은 현대적인 언어이므로 가비지 콜렉터가 사용하지 않는 메모리를 자동으로 해제해 줍니다. 따라서 개발자는 메모리 관리에 신경 쓰지 않고 로직 작성에만 집중할 수 있습니다. 설명 끝.

이면 좋겠지만 현실은 그렇게 간단하지 않는 법입니다. C#이 메모리 관리를 자동으로 해주는 것이 맞지만 우리가 사용하는 리소스가 메모리만 있는 것은 아닙니다. 예를 들어, 운영 체제는 메모리와 별개로 최대로 열 수 있는 파일 개수에 제한이 존재합니다. 리눅스의 경우, /proc/sys/fs/file-max 파일 출력해 보면 최대로 열 수 있는 파일 개수를 알 수 있습니다.

$ cat /proc/sys/fs/file-max
75000

바꿔 말하면 메모리가 많이 남아 있더라도 파일을 75000개 이상 열면 더 이상 파일을 열지 못하는 상황이 발생합니다. 이 문제를 해결하려면 C, C++ 시절 메모리 관리와 마찬가지로 더 이상 사용하지 않는 파일을 수동으로 닫아줘야 합니다.

예를 들어, 우리가 텍스트 파일을 읽을 때 사용하는 StreamReader 클래스는 파일을 열기 때문에 사용 후에는 반드시 Close 메소드를 호출해서 닫아줘야 합니다. 여기서 Close 메소드가 하는 일이 열려 있는 파일을 수동으로 닫아주는 것입니다. 만약, Close를 실수로 안 불러줬다면 파일이 닫기지 않아 메모리는 남아 돌지만 더 이상 파일을 열지 못하는 상황이 올 수도 있습니다.

var reader = new StreamReader("file.txt");
var content = reader.ReadToEnd();
// Do something with content
reader.Close();

이 코드는 버그가 있습니다. 만약 ReadToEnd 메소드나 content를 사용하는 코드에서 예외가 발생했다면reader.Close() 메소드가 불리지 않고 빠져나갈 수 있습니다. 정상 종료든 예외 상황이든 파일을 꼭 닫기 위해서는 다음과 같이 finally 블록을 사용해야 합니다.

var reader = new StreamReader("file.txt");
try {
    var content = reader.ReadToEnd();
    // Do something with content
} finally {
    reader.Close();
}

이런 식으로 블록을 빠져 나갈 때 리소스를 수동으로 해제해야 하는 경우가 많기 때문에 C#은 using이라는 키워드를 제공합니다. 위 코드를 using을 이용해 표현해 보면 다음과 같습니다. using 블록을 빠져나갈 때 자동으로 reader를 해제하기 때문에 더 이상 finally 블록에서 Close 메소드를 호출해 줄 필요가 없게 되었습니다.

using (var reader = new StreamReader("file.txt"))
{
    var content = reader.ReadToEnd();
    // Do something with content
}

그런데 C#은 각 리소스를 해제하는 방법을 어떻게 아는 걸까요? StreamReader의 경우는 Close 메소드를 부르면 되지만, 리소스 종료에 따라 해제하는 방법이 다를 수밖에 없습니다. 이런 문제를 해결하려면 각 리소스 클래스가 통일된 인터페이스를 구현해줘야 할 것입니다.

.NET에 이런 목적으로 존재하는 인터페이스가 IDisposable입니다. IDisposableDispose 메소드를 정의하고 있는데, IDisposable을 구현한 클래스가 using 블록에 사용되면 using 블록을 빠져나갈 때 C#이 자동으로 Dispose 메소드를 불러 줍니다.

public interface IDisposable
{
   void Dispose()
}

앞서 살펴본 StreamReader 클래스도 베이스 클래스인 TextReader를 통해 IDisposable 인터페이스를 구현하고 있고, Dispose 메소드에서 Close를 호출하기 때문에 using 블록을 빠져나갈 때 자동으로 리소스가 해제되는 것입니다.

.NET에서는 명시적인 리소스 해제가 필요한 클래스가 모두 IDisposable 인터페이스를 구현하고 있기 때문에 이런 클래스를 사용할 때는 될 수 있으면 using 블록을 사용하는 것이 좋습니다. 또한 우리가 만드는 클래스도 명시적인 리소스 해제가 필요할 경우 IDisposable를 구현하는 것이 좋은 습관입니다.

One thought on “Using 키워드와 IDisposable

  1. Pingback: Using 키워드와 IDisposable | 서광열의 C# 스쿨

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s