C#에서 박싱(Boxing)과 언박싱(Unboxing)은 값 타입과 참조 타입 간의 변환 과정을 말합니다.
박싱(Boxing)
정의: 박싱은 값 타입(예: int, double, struct 등)을 참조 타입 객체인 System.Object나 다른 인터페이스 타입으로 변환하는 과정입니다.
발생 시점: 이 변환은 값 타입이 힙 메모리에 저장되어야 할 때 발생합니다.
메모리에서 발생하는 일:
- 메모리 할당: 박싱 과정에서 힙에 새로운 객체를 위한 메모리를 할당합니다.
- 데이터 복사: 값 타입의 데이터는 스택에서 힙으로 복사됩니다.
- 참조 생성: 힙에 저장된 객체를 가리키는 참조가 생성되고, 이 참조는 스택에 저장됩니다.
예시 코드:
int i = 123;
object o = i;
언박싱(Unboxing)
정의: 언박싱은 박싱된 객체를 다시 원래의 값 타입으로 변환하는 과정입니다.
발생 시점: 언박싱은 명시적 캐스팅을 통해 수행되며, 참조 타입에서 값 타입으로 데이터를 복사합니다.
메모리에서 발생하는 일:
- 타입 검사: 언박싱하기 전에, 실제 객체의 타입이 목표 타입과 일치하는지 확인합니다.
- 데이터 복사: 힙에서 스택으로 데이터가 복사됩니다.
예시 코드:
object o = 123;
int i = (int)o;
성능 문제 및 대처 방법
박싱(Boxing)과 언박싱(Unboxing)은 C#에서 필요한 작업이지만, 성능에 부정적인 영향을 줄 수 있습니다. 이 두 과정이 성능 문제를 일으키는 주된 이유는 다음과 같습니다:
1. 메모리 할당과 복사
- 박싱: 값 타입을 참조 타입으로 변환할 때, 해당 데이터를 스택에서 힙으로 복사해야 합니다. 힙 메모리에 새로운 객체를 위한 공간을 할당해야 하며, 이 과정에서 추가적인 메모리 사용과 CPU 시간이 소모됩니다. 힙 메모리는 스택 메모리에 비해 관리 비용이 높고, 메모리 할당과 해제가 더 복잡합니다.
- 언박싱: 박싱된 객체를 다시 값 타입으로 변환할 때는 힙에 저장된 데이터를 스택으로 복사해야 합니다. 이 과정에서도 메모리 접근과 데이터 복사가 발생하여 성능에 영향을 미칩니다.
2. 가비지 컬렉션 부하 증가
- 박싱된 객체는 힙 메모리에 저장되므로 가비지 컬렉터(GC)의 대상이 됩니다. GC는 더 이상 필요하지 않은 메모리를 정리하는 작업이며, 이 과정에서 프로그램 실행이 일시적으로 중단될 수 있습니다("Stop-the-World"). 박싱이 빈번하게 발생하면 가비지 컬렉터의 부하가 증가하고, 이는 전체 시스템의 응답성과 성능 저하로 이어질 수 있습니다.
3. CPU 사용 증가
- 박싱과 언박싱 모두 CPU 리소스를 사용하는 연산입니다. 특히, 타입 검사와 같은 추가적인 연산이 필요한 언박싱 과정에서는 CPU 사용량이 더욱 증가할 수 있습니다. 이러한 추가적인 연산은 애플리케이션의 처리 능력을 저하시킬 수 있습니다.
대처 방법
- 제네릭 사용: 가능하다면 제네릭 컬렉션과 메서드를 사용하여 타입-안전성을 유지하면서 박싱을 피합니다. 예를 들어, List<int>는 ArrayList보다 선호되며, 이는 int를 저장할 때 박싱이 발생하지 않기 때문입니다.
- 박싱 피하기: 값 타입을 참조 타입으로 변환할 필요가 없는 경우, 박싱을 피하도록 코드를 설계합니다.