들어가기에 앞서 ‘object’ 타입에 관해 간단하게…
C# 을 이용하여 코딩을 하면서, 혹은 유니티 스크립트를 짤때 ‘object’ 라는 자료형을 본적이 있는가? 이 자료형은 어떤 타입의 변수든 할당하여 사용할수 있다.
object number = 10; objcet str = “CodingGom”;
과 같이 말이다. 마치 C/C++ 의 ‘auto’ 나 C#의 ‘var’ 처럼 말이다. 물론 같은것은 아니다. 전혀 다른 개념이다. auto, var 키워드는 타입을 추론하는 자동변수들이다. 런타임이 아닌 컴파일단계에서 타입이 정해지도록 지정해 놓는 녀석들인 것이다. 즉, 타입을 ‘명시적’으로 지정하지 않아도 대입된 값에 따라 컴파일러가 추론을 해준다는 것이다.
여기서 키워드는 ‘명시적’ 이다. object 타입은 어떤 값이든 대입할때는 ‘명시적’ 으로 타입을 지정하지 않아도 되지만, 사용할때는 타입을 ‘명시’ 해주어야 한다. 아래의 예르 보자
object obj = 30.0f; float temp1 = obj; // error float temp2 = (float)obj; // non-error
차이점이 보이는가? object 타입의 값을 사용할때는 항상 ‘명시적’ 으로 타입을 지정해줘야 한다. 자, 그럼 왜 이렇게 되는것일까?
Boxing / Unboxing (박싱 / 언박싱)
물건을 담을 상자가 하나 있다. 이 상자에는 어떤 물건이든 담을수 있을것이다. 사실 담는 물건이 중요한건지 상자가 중요한게 아닐것이다. 상자에 어떤걸 담느냐에 따라 상자의 가치는 변한다.
여기서 상자가 object 타입의 변수이고, 담는 물건이 object 타입을 결정할 값이다. 즉, 상자인 object 타입의 변수에 어떤 (가치)물건을 담든 잘 사용할수 있다는 것이다. 상자는 단지 물건을 담고(참조) 있을 뿐이다. 사용할때 어떤 물건인지 알려주면(명시적 타입 지정) 된다.
Boxing(박싱) 이란 단어 그대로 물건(값 형식)을 박스(참조 형식)에 넣어주는것을 말한다. 값 형식 => 참조 형식
Unboxing(언박싱) 이란 단어 그대로 박스(참조 형식)에서 물건(값 형식)을 꺼내는 것을 말한다. “이 상자에는 어떤 가치(타입) 의 물건이 들어있으니 꺼내라” 라는 것이다. 참조 형식 => 값 형식
objcet 형이 이러한 작동이 가능한 이유는, 모든 타입(int, float, string, char, 구조체…) 의 최상위 부모이기 때문이다.(object = System.Object / 상속) c/c++ 에서 최상위 부모로 포인터를 받고, 캐스팅해서 사용했던 포인터의 개념을 생각하면 쉬울것이다.
어떤 타입이든 넣고 사용할때만 명시해서 사용하고 싶을때 object 타입을 사용하면 참 편리할것이다.
Boxing / Unboxing (박싱 / 언박싱) 의 단점
몸에 좋은 약은 쓰고, 달콤한건 몸에 해롭다는 말이 있다. (달콤한게 정신건강에는 좋은거 같다) 아무리 편리하다고 해도, 편리에는 그만한 비용이 따른다. 사용할때마다 타입을 캐스팅해서 써야 하기 때문에 Boxing / Unboxing (박싱 / 언박싱)은 부하가 걸리는 주된 원인이 된다.
위에서 설명하길 Boxing / Unboxing (박싱 / 언박싱) 은 참조 형식 <=> 값 형식 의 과정이라고 했다. 즉, 힙과 스택을 오가며 메모리 복사가 일어난다는 것이다.
Boxing 과정에서는 스택에 저장되어있는 값을 힙에 복사하고, 힙에 복사된 값을 object가 참조한다. Unboxing 은 참조하고 있는 object 를 타고가, 힙에 있는 값을 복사하여 스택으로 가져와 사용한다. 이과정이 상당히 비싼 비용을 지불해야 한다… 성능적으로…
마무리
Boxing / Unboxing (박싱 / 언박싱) 은 편할지 모르지만, 비용이 비싸서 잘못 사용할경우 성능 부하가 걸린다. 메모리 복사가 빈번하게 일어나다 보니 그에 따른 비용과 임시 값들을 사용하기 때문에 가비지 컬렉션에도 걸린다. 당연한 이야기지만 Boxing 되지 않은 변수는 Unboxing 해서 사용할 수 없다. 있지도 않은 상자를 풀어헤칠수는 없으니 말이다.