Hello World

자바 Primitive Type과 Boxed Primitives 둘 중 무엇을 사용할까? 본문

Java/Core

자바 Primitive Type과 Boxed Primitives 둘 중 무엇을 사용할까?

EnterKey 2016. 1. 11. 18:19
반응형

자바에는 기본 자료형인 Primitive Type과 객체로 제공되는 Boxed Primitives Type이 있다. (보통 Primitive를 감싸고 있다고 해서 Wrapper 클래스라고도 불린다.)

자바 1.5 이상부터 Primitive와 Boxed Primitives간의 변환(Cast)을 자동으로 해주는 Autoboxing/AutoUnboxing을 지원하면서 편리하게 사용할 수 있다.

그럼 Primitive와 Boxed Primitives 둘 중 무엇을 사용해야 할까? 스택 영역에 저장되는 Primitive(기본자료형)과 힙영역에 저장되는 Boxed Primitives(객체형)간에는 메모리 효율과 접근속도면에서 Primitive Type이 뛰어나다.

그럼 언제 Boxed Primitives를 사용하는게 좋을까? 첫째, 명시적으로 null을 반환해야 할 경우. Primitive Type의 경우 기본 값을 가지고 있으므로 원하지 않는 값이 제공 될 수 있다. (int는 0, boolean은 false 등) 둘째, Collection의 키/값으로 사용되어 질 때이다. Collection에는 Primitive를 넣을 수 없기 때문에 객체형인 Boxed Primitives를 사용해야한다. 셋째, Parameterized Type의 경우도 (예로 List<String>에서 <String>을 말한다) Boxed Primitives를 사용해야 한다.

이외의 경우에는 Primitive Type을 선호한다.

Boxed Primitives의 경우 성능뿐 아니라 다음과 같은 상황에서 문제가 발생 할 소지가 있기 때문이다. 첫째, == 등 비교 연산에 주의하자. 객체에서의 ==은 값의 비교가 아니라 레퍼런스의 비교이다. Autoboxing/AutoUnboxing으로 인해 혼돈 할 수 있으니 주의하자. (레퍼런스 비교임에도 불구하고 Caching을 하고 있어서 일부 범위에서는 예상 외의 결과가 나온다. 자세한 건 뒤에서 설명하겠다.) 둘째, Autoboxing/AutoUnboxing으로 인해 불필요한 성능저하를 초래할수 있다. 아래 코드 반복문 안의 sum = sum + i; 수행시 Boxed Primitives가 AutoUnboxing되어 매번 Primitives로 변환되어 계산된다. 에러는 없는 코드이지만 반복적인 AutoUnboxing으로 성능저하가 발생하게 된다.

1
2
3
4
5
6
7
public static void main(String[] args) {
    Long sum = 0L;
    for (long i=0; i<Integer.MAX_VALUE; i++) {
        sum += i;
    }
    System.out.println(sum);
}

Boxed Primitives에서 또 하나 특이한 점은 Caching을 하고 있다는 사실이다.

1
2
3
4
5
6
7
Integer i1 = 127;
Integer i2 = 127;
Integer j1 = 128;
Integer j2 = 128;
 
System.out.println(i1 == i2);
System.out.println(j1 == j2);

결과는 무엇일까? 답은 true와 false다.

Integer i1 = 127 은 컴파일시에 Integer i1 = Integer.valueOf(127)로 변환된다. valueOf 메소드에서는 해당 int값에 대한 Integer 객체를 생성해서 반환한다. 응? 그럼 어쨋든 ==는 객체 레퍼런스 비교이기 때문에 둘 다 false여야 되는게 아닌가? 여기서 우리가 알아 두어야 할 점이 바로 Caching이다.

Boxed Primitives에서는 자주 사용되는 범위의 값에 대해 미리 생성 해 놓은(Caching 된) 객체를 가지고 있다. 실제 Integer.java 소스를 확인하면 다음과 같이 내부적으로 Caching 된 Integer 객체를 반환한다. (주석을 보면 Caching 범위는 JVM Option으로 조절 할 수 있다.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public static Integer valueOf(int i) {
    assert IntegerCache.high >= 127;
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}
 
/**
 * Cache to support the object identity semantics of autoboxing for values between
 * -128 and 127 (inclusive) as required by JLS.
 *
 * The cache is initialized on first usage.  The size of the cache
 * may be controlled by the -XX:AutoBoxCacheMax=<size> option.
 * During VM initialization, java.lang.Integer.IntegerCache.high property
 * may be set and saved in the private system properties in the
 * sun.misc.VM class.
*/
private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static final Integer cache[];
     
    static {
        // high value may be configured by property
        int h = 127;
        String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            int i = parseInt(integerCacheHighPropValue);
            i = Math.max(i, 127);
            // Maximum array size is Integer.MAX_VALUE
            h = Math.min(i, Integer.MAX_VALUE - (-low));
        }
        high = h;
  
        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);
    }
     
    private IntegerCache() {}
}

-128 ~ 127 범위에 대해 객체 Caching을 해 놓고 있다. 결국 저 범위의 숫자에 대해서 반환 된 객체는 Caching된 동일 객체이므로 == 비교에서 true인 것이다. Autoboxing/AutoUnboxing에 대해 혼돈하고 Caching된 범위의 값들로만 == 테스트를 해보았다면 사소하지만 큰일이 발생 할 수도 있으니 주의하자.

[참고]
Effective Java 2nd Edition : Item 49. Prefer primitive types to boxed primitives
Java Language Specification : 5.1.7. Boxing Conversion (http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html)

[추가 참고]

http://psvm.tistory.com/81

http://psvm.tistory.com/82

반응형
Comments