박싱

값 타입(Value Type)이나 참조 타입(Reference Type)을 System.Object로 변환하는 작업.

 

언박싱

박싱한 System.Object 타입을 박싱 이전의 타입으로 변환하는 작업.

 

그중 오늘은 값 타입의 박싱 연산, 언박싱 연산에 대해 포스팅을 하겠다.

 

값 타입과 참조 타입에 대해서는 이전 포스팅을 참고하길 바란다.

computervision-is-fun.tistory.com/21

 

값 타입, 참조 타입

1. 값 타입 (struct, enum)  - new 예약어를 통해 할당시킨 값 타입은 스레드 스택 메모리에 저장되므로, 스레드가 종료될 시 스택의 순서대로  삭제 된다. (GC의 오버헤드가 없다)  - = 연산시 필드 값

computervision-is-fun.tistory.com

 

값 타입의 박싱

값 타입은 생성 시 스레드의 스택 메모리에 저장되고, 참조 값이 아닌 값 자체를 다룬다.

참조 타입과 여러모로 대조가 된다.

이런 설명을 전재로 아래와 같이 값 타입을 ArrayList에 저장시키면 어떤 동작이 일어나까??

struct Point
{
    int nPtX;
    int nPtY;
    
    public Point(int nInputPtX, int nInputPtY)
    {
    	nPtX = nInputPtX;
        nPtY = nInputPtY;
    }
}

public static void Main()
{
    Point pt = new Point(1,2);
    ArrayList arrList = new ArrayList();
    
    arrList.Add(pt);
}

ArrayList의 Add함수를 살펴보자

public virtual Int32 Add(Object Value);

이처럼 Add함수는 Object를 매개변수로 동작한다.

그리고 위 코드는 참조 타입 매개변수에 값 타입을 넣어도 동작이 되었다.

왜 그럴까??

이유는 값 형식의 Point를 참조 형식의 Object로 박싱 명령을 내부적으로 진행했기 때문이다.

박싱은 내부적으로 어떤 동작을 할까??

어떻게 스레드 스택 메모리에 저장되어있는 데이터가 관리되는 힙 메모리에 저장되는 것일까?

순서는 이렇다.

 

1. 관리되는 힙 메모리에 Point의 필드 멤버를 고려하여 메모리가 할당된다. 이때 참조 형식이므로 타입 객체 포인터와 동기화 블록 인덱스라는 필드를 포함하여 할당한다.

2. 값 타입인 Point의 필드 멤버 변수들을 관리되는 힙 메모리에 복사시킨다.

3. 새로 생성된 참조 타입의 Point의 포인터를 반환한다.

 

이제 실제 위의 동작을 하는지 ILDASM.EME(역어 셈)으로 실행해보겠다.

위의 코드를 역어셈, IL_0011~IL0012가 박싱 동작

아쉽게도 더욱 자세한 코드는 보기 힘들 것 같다. 하지만 위와 같이 box라는 부수적인 명령어가 붙으면서 우리는 박싱 동작을 짐작할 수 있다. 박싱 동작을 더욱 자세히 확인할 수 있는 방법은 더 찾아봐야겠다.

 

추가로 박싱 된 참조 타입의 Point는 기존의 값 타입의 Point보다 생명주기가 길다.

이유는 관리되는 힙 메모리가 저장되었기 때문에 다음 GC의 호출을 기다리기 때문이다.

 

값 타입의 언박싱

Object에 저장되어있던 참조 형식의 Point가 값 형식의 Point로 변환하는 작업을 말한다.

CLR은 언박싱을 수행하면 아래와 같은 동작을 한다.

 

1. 관리되는 힙 메모리에서 참조 형식의 Point의 필드 값들의 주소를 가져온다.

  (ex nPtX, nPtY)

2. 이 값들은 스레드 스택 메모리의 Point에 복사시킨다.

 

위의 과정과 아래의 코드를 역어 셈으로 보겠다.

static void Main(string[] args)
{
    Point pt = new Point(1, 2);
    Object o = pt;
    pt = (Point)o;
}

 

IL_0012가 언박싱 동작

IL_0012 명령 줄을 보면 unbox라 하고 Object를 Point의 값 타입으로 복사시킨 것을 알 수 있다.

 

결론

값 타입의 박싱과 언박싱 모두 의도하지 않게 복사 동작을 진행한다. 따라서 우리는 값 타입의 박싱과 언박싱을 최소화해야 한다. 

 

 

 

 

 

 

 

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

const 상수  (1) 2020.12.20
값 타입, 참조 타입  (0) 2020.12.13
네임스페이스와 어셈블리  (1) 2020.12.05
타입에 대하여2  (0) 2020.12.02
타입에 대하여1  (1) 2020.11.29

+ Recent posts