brunch

You can make anything
by writing

C.S.Lewis

by 이승현 Sep 11. 2017

Beware Autoboxing

Beware Autoboxing


#01 Java Data Types


Java 자료형(Data Type)에는 기본형(Primitive Type)과 참조형(Reference Type)이 있습니다.

Java 자료형에 대한 내용보다는, 이 두 자료형 사이에서 일어나는 Autoboxing으로 인한 이슈와 이를 해결할 수 있는 대응법에 대해 알아보겠습니다.


두 자료형의 자세한 개념은 아래 링크를 참고해 주시기 바랍니다.




Autoboxing


#02 Autoboxing


Autoboxing이란, 기본형(Primitive Type)과 해당 wrapper 객체 간 변환을 Java 컴파일러가 자동으로 해주는 것을 말합니다.

Boxing : 기본형을 해당 참조형으로 변환. (int -> Integer)
Unboxing : 참조형을 해당 기본형으로 변환. (Integer -> int)


따라서 개발자가 직접 타입 변환을 명시하지 않아도 컴파일러가 자동으로 해주기 때문에 편해집니다.




Autoboxing Issues


#03 Use Integer object in for loop


for문을 이용한 0부터 100까지의 합을 구하는 코드입니다. 

for문 코드를 보면, 컴파일러가 자동으로 참조형인 Integer 변수를 기본형으로 Autoboxing 해주기 때문에 기본형인 int 변수를 이용했을 때와 동일한 결과를 얻을 수 있습니다.

 

겉보기엔 Autoboxing으로 인해 두 for문이 똑같아 보이지만 내부 로직을 보면에 몇 가지 이슈가 있습니다.




#01 Allocation Overhead


#04 Java Data Types with Memory


우선 기본형은 비 객체 타입이고 참조형은 객체 타입이기 때문에, 데이터를 저장하는 방법이 서로 다릅니다.

기본형 : Stack Memory에 실제 값을 저장
참조형 : Stack Memory에 객체의 주소를 저장, 객체는 Heap Memory에 저장


#05 Allocation overhead


따라서 내부 로직을 보면 for문에서 Integer 변수를 쓸 때마다 Integer 객체를 새로 생성하고, 거기에 int 값을 넣고, 기존 Integer value 객체에 값을 더하는 계산을 해야 합니다.

1. Create new Integer object.
2. Push new value to new Integer object.
3. Add to Integer value variable.


즉, Autoboxing을 할 때마다 새로운 객체 할당을 해야 합니다.

기본형을 썼을 때는 필요 없는 객체 할당 비용을 매번 해야 하는 이슈가 발생합니다.




#02 Memory Overhead


#06 Memory overhead


그리고 참조형(Integer)은 기본형(int)보다 크기도 더 큽니다.

int의 크기는 4 bytes인데 Integer는 16 bytes입니다.

따라서 기본형을 썼을 때보다 메모리 오버헤드 이슈도 발생합니다.



#07 why can't be 100?
for문으로 100개의 Integer 객체를 생성했는데 실제로는 84개만 만들어졌습니다.
안드로이드 런타임 캐시 때문이라는 의견도 있는데 저도 정확히는 모르겠네요.
혹시 아시는 분 피드백 부탁드립니다.




Generic Collection with Autoboxing


#08 Boxing & UnBoxing


개발자가 조금만 신경 쓴다면 위와 같은 Autoboxing으로 인한 이슈를 미리 방지할 수 있습니다.


하지만 Generic Collection에서는 참조 형만 이용할 수 있기 때문에 Generic Collection을 기본형과 같이 이용한다면 Autoboxing은 필연적으로 발생하게 됩니다.

Generic : 클래스 내부에서 이용할 자료형을 외부에서 지정하는 기법
Collection : 여러 요소의 그룹을 하나의 단위로 엮는 객체이고 다양한 인터페이스를 제공
ex) Set<Integer> integerSet = new HashSet<>();
ex) List<Boolean> booleanList = new ArrayList<>();



#09 Autoboxing with HashMap


HashMap과 같은 Generic Collcetion에서는 Autoboxing이 발생할 수 있습니다.

HashMap<Object, Object>은 Key와 Value에 객체-참조 형만 이용할 수 있습니다.

따라서 HashMap에 기본형 값을 추가, 수정, 삭제 시, 컴파일러는 자동으로 참조형으로 Autoboxing 합니다.




SparseArray


#10 SparseArray


안드로이드에서는 Generic Collection에서 발생할 수 있는 Autoboxing 이슈를 방지하기 위한 대안으로 SparseArray라는 자체 컨테이너를 지원합니다.

SparseArray 계열은 처음부터 Autoboxing 이슈 때문에 설계되었습니다.


#11 Use SparseArray


따라서 Autoboxing 이슈를 방지하기 위해서 아래와 같이 SparseArray 컨테이너들을 이용하는 게 좋습니다.

#12 Recommend SparseArray





Allocation Tracker


#13 Allocation Tracker


Autoboxing이 어디서 얼마나 일어나는지 정확히 알고 싶다면 안드로이드 스튜디오에서 제공하는 Allocation Tracker 기능을 이용하시면 됩니다.


이용법에 대한 자세한 내용은 아래 링크를 참고해 주시기 바랍니다.







그동안 SparseArray가 성능면에서 HashMap보다 더 좋다는 점은 인지하고 있었는데 구체적인 이유는 이제야 알았네요.

앞서 설명대로 Autoboxing 이슈를 해결할 수 있지만, SparseArray를 이용할 때 고려해야 할 점도 있습니다.

자세한 내용은 다음 글에 기재할게요.


브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari