(ing)Dispose 패턴

GC가 있는데 왜 IDisposable을 써야 할까?

GC가 메모리를 알아서 정리해주는데 왜 명시적으로 리소스를 정리해줘야하는지 이 때, disposable 패턴이 어떻게 사용되는지 알아보자

C#의 GC는 모두가 잘 알고있다시피 Heap에 생성된 객체 중 더 이상 참조되지 않는 객체들을 자동으로 수거해준다.

하지만 GC가 수거하지 않는 대상이 있다. 바로 unmanaged resource 인데, 이런 리소스들은 실제로는 OS 레벨에서 핸들링되는 OS 자원이고 GC는 그 내부에 열려있는 소켓이나 리소스 동작에 대해 알 수 없기 때문에 해당 리소스가 랩핑된 객체 정도만 수거할 수 있다.

unmanaged resource

  • 파일 핸들 (FileStream)

  • DB 커넥션 (SqlConnection)

  • 네트워크 소켓 (TcpClient)

  • 허브/웹소켓 커넥션 (HubConnection)

  • 타이머, 쓰레드, 핸들, 윈도우 핸들 등 OS 리소스

따라서 위 리소스들을 명시적으로 해제해주지 않으면 소켓이 열려있거나, db 커넥션 풀을 다 써버리거나 리소스 누수로 이어져 심하면 OOM 등 전체 프로세스에 큰 영향을 줄 수가 있다.

그래서 등장한 것이 바로 IDisposable 패턴이다.

C# 에서의 자원 해제

C#의 using 구문은 IDisposable을 구현한 객체에 대해 블록 종료 시 자동으로 Dispose()를 호출한다.

using (var stream = new FileStream("file.txt", FileMode.Open))
using (var reader = new StreamReader(stream))
{
    string? line;
    while ((line = reader.ReadLine()) != null)
    {
        Console.WriteLine(line);
    }
}
etc.

Java에서의 자원 해제

Java 7부터 도입된 try-with-resources 문법은 AutoCloseable 인터페이스를 구현한 객체에 대해 블록 종료 시 자동으로 close()를 호출한다.

try (
FileInputStream is = new FileInputStream("file.txt"); 
BufferedInputStream bis = new BufferedInputStream(is)
) {
        int data;
        while ((data = bis.read()) != -1) {
            System.out.print((char) data);
        }
  }

Kotlin 에서의 자원 해제

ByteArrayInputStream(byteArray).use { stream ->
}
// 내부적으로 AutoCloseable.close() 호출. java7 try-with-resources 와 동일

Last updated