using 문이란 C# 문법에서 메모리 사용을 효율적으로 관리할 수 있게 도와주는 구문 입니다.
리소스 자동 해제 (IDisposable)
using 블록을 사용하면, 객체 사용이 끝났을 때 자동으로 Dispose()가 호출되어 리소스가 해제됩니다.
주로 파일, DB 연결, 스트림 등 외부 리소스를 다룰 때 사용합니다.
using (var reader = new StreamReader("test.txt"))
{
string line = reader.ReadLine();
Console.WriteLine(line);
}
// 여기서 reader.Dispose()가 자동 호출됨 (파일 핸들 해제)
Dispose는 리소스를 사용하였을 때 해제를 하는 함수이다.
Dispose()를 해야 하는 이유는 외부 리소스를 안전하게 정리하기 위해서입니다. C#은 자동 메모리 관리를 해주는 가비지 컬렉터(GC)가 있지만, 가비지 컬렉터가 다루지 못하는 것들이 있습니다.
조금 더 자세하게 다뤄 보자면
Dispose()를 해야 하는 이유
1. 관리되지 않는 리소스(Unmanaged Resources) 정리
- 예: 파일 핸들, 데이터베이스 연결, 네트워크 소켓, 그래픽 리소스 등
- 이런 리소스들은 가비지 컬렉터가 자동으로 해제하지 않음.
- Dispose()를 호출해야 OS에 리소스 반환.
2. 리소스를 오래 붙잡고 있으면 문제 발생
- 예를 들어, 파일을 Dispose()하지 않으면:
- 다른 프로그램이나 코드가 그 파일을 열 수 없음.
- "파일이 사용 중입니다" 오류 발생 가능.
3. 메모리 누수 및 성능 저하 방지
- 관리되지 않는 리소스는 GC가 해제하지 않기 때문에, 메모리 누수(leak)가 생길 수 있음.
- 리소스를 많이 열고 닫는 작업에서 Dispose() 안 하면 성능이 급격히 저하됨.
4. IDisposable 패턴으로 리소스 명확하게 정리 가능
- IDisposable 인터페이스는 명시적으로 Dispose()를 호출하도록 만들어졌고,
- using 문법을 통해 try-finally 없이 깔끔하게 처리 가능함.
{
var content = reader.ReadToEnd();
}
이 코드에서 일어나는 일:
- StreamReader는 FileStream을 내부적으로 사용해서 파일을 엽니다.
- 이때 *파일 핸들(파일을 가리키는 포인터 같은 것)이 열립니다. 이건 운영체제(OS)의 리소스!
- ReadToEnd()로 읽은 텍스트 데이터는 메모리(RAM)에 로드됩니다.
- 하지만 FileStream과 연결된 핸들은 명시적으로 닫아야 안전합니다.
- 안 닫으면: 다른 코드/프로그램이 그 파일을 열 수 없을 수 있어요. "파일이 사용 중" 오류!
using은 자동으로 Dispose() 호출 → 파일 핸들 닫음
=> 그래서 더 이상 파일이 OS 리소스를 점유하지 않게 됨.
그럼 메모리는 어떻게 될까?
- ReadToEnd()로 읽은 문자열 데이터는 여전히 메모리에 남아 있음.
- 이건 C#의 가비지 컬렉터가 관리하는 "관리되는 메모리"이고, 이 데이터는 변수를 더 이상 사용하지 않으면 GC가 나중에 알아서 수거합니다.
리소스 종류 어디에? 해제 방법
파일 핸들 | OS 리소스 | Dispose() 필요 (using으로 처리) |
파일 내용(텍스트) | 메모리(RAM, 관리됨) | GC가 자동 수거 |
그럼 파일 스트림이란 뭔가?
파일 스트림(FileStream)이란?
파일에 데이터를 읽거나 쓰기 위해 운영체제와 연결된 통로(스트림)
조금 더 비유하자면:
- 파일 = 물탱크
- 파일 스트림 = 그 물탱크에 연결된 수도관
- 데이터 = 물 (파일 안의 내용)
우리는 수도관(파일 스트림)을 통해 물탱크(파일)에서 물(데이터)을 꺼내오거나, 흘려보내는 거죠.
기술적으로 말하면?
- FileStream은 .NET에서 제공하는 클래스입니다.
- 이 클래스는 하드디스크에 있는 파일을 열고, 읽거나 쓰는 기능을 제공합니다.
- 내부적으로는 OS에 파일 핸들(handle)을 요청해서 연결을 만듭니다.
FileStream fs = new FileStream("test.txt", FileMode.Open);
이 코드는: "test.txt"라는 파일을 열고 fs라는 스트림 객체를 통해 바이트 단위로 데이터를 읽거나 쓸 수 있게 해줍니다.
그럼 이러한 파일들을 왜 외부 리소스라고 부르는가?
외부 리소스 = .NET이나 CLR이 직접 관리하지 않는 리소스
- 메모리는 .NET이 GC로 관리 → 내부 리소스 (managed)
- 하지만 파일, 네트워크, DB 연결, 그래픽 핸들 등은 OS가 관리
→ 이건 외부 리소스 (unmanaged)
FileStream은 이 unmanaged 리소스를 다루는 "관리자 역할"을 하는 거예요.
그래서 다 쓰고 나면 Dispose()를 호출해서 OS에 "다 썼어요!"라고 알려야 합니다.
파일 스트림을 정리하자면
- 파일을 읽고 쓰기 위한 클래스다.
- 내부적으로 운영체제의 파일 핸들을 사용한다.
- 그래서 외부 리소스이며, 사용 후 반드시 닫아야(Dispose) 한다.
- 그 안에서 읽은 텍스트 내용은 메모리에 올라가며, GC가 나중에 정리한다.
Using 문을 정리하자면
외부 리소스인 파일을 읽으려면 파일 스트림(FileStream) 을 통해 열어야 하고,
이 스트림은 운영체제(OS)의 자원(파일 핸들) 을 사용하기 때문에
다 사용한 뒤에는 꼭 닫아줘야 합니다. (Dispose() 사용 또는 using으로 (using문은 자동으로 Dispose가 내장되어 있음))
파일 스트림을 통해 읽어온 텍스트 데이터는 메모리(RAM) 에 올라가며,
이 데이터는 가비지 컬렉터(GC)가 자동으로 정리합니다.
따라서 따로 해제하지 않아도 되지만, 스트림은 반드시 명시적으로 닫아야 합니다.
+ 여기서 추가적인 정보를 더 붙이자면
FileStream하고 UnityWebRequest와의 차이를 조금 더 설명해보겠다.
FileStream은 OS 리소스를 직접 사용하는 파일 입출력 통로라서, 사용 후 반드시 Dispose로 닫아줘야 해. 반면 UnityWebRequest는 네트워크 통신을 Unity가 내부적으로 관리해주기 때문에, 반드시 Dispose할 필요는 없지만 자주 사용되거나 리소스를 아끼고 싶다면 using을 쓰는 것도 좋은 습관이다.
왜 UnityWebRequest에 using을 쓰는 게 좋을까?
Unity는 자동으로 리소스를 정리해준다
- UnityWebRequest는 내부적으로 통신이 끝나면 Dispose()가 호출되도록 설계되어 있음
- 그래서 간단한 요청에서는 굳이 신경 안 써도 돼
하지만 리소스 정리가 “언제” 되는지는 불명확함
- Unity가 내부적으로 언제 Dispose()를 호출하는지 명확한 시점을 알 수 없음
- GC 타이밍, MonoBehaviour의 생명 주기, 네트워크 상황 등에 따라 딜레이될 수 있음
그래서 문제가 생기는 경우는?
- 게임 내에서 수십, 수백 번 요청을 날리는 경우
- 이미지, 사운드, JSON 등을 연속적으로 여러 번 다운로드할 때
- 모바일 기기처럼 리소스에 민감한 환경일 때
이런 상황에서 Dispose()가 늦게 되면:
- 메모리 누수(memory leak) 처럼 보이는 현상 발생
- 텍스처나 다운로드된 파일이 메모리에 계속 유지
- 심하면 앱이 뻗거나, GC가 폭주해서 프레임 드랍 발생
그래서 using을 쓰는 이유는?
내가 직접 명확하게 리소스 해제를 컨트롤하기 위해서이다.
예)
using (UnityWebRequest req = UnityWebRequest.Get(url))
{
yield return req.SendWebRequest();
// 데이터를 사용
}
// 여기서 명확하게 req.Dispose() 실행됨 → 리소스 확실하게 해제
'C#' 카테고리의 다른 글
C# - Coroutine (0) | 2024.07.06 |
---|---|
C# - 2 (0) | 2024.05.19 |
C# - Custom Type2 (2) | 2024.04.11 |
Virtual Table (0) | 2024.04.11 |
Class (0) | 2024.04.09 |