NoTimeForDawdling

Wrapper class Cache 파헤치기 본문

Java

Wrapper class Cache 파헤치기

Room_Energy 2021. 2. 13. 17:06

Java에 애플리케이션의 성능 향상을 위한 Cache로직이 종종 사용됩니다.

실제로 Integer 클래스는 내부에서 Integer 사용을 위해 IntegerCache를 관리합니다.

IntegerCache 정적 클래스

public final class Integer extends Number implements Comparable<Integer> {
    
    ...
    
     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) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }
  
    ...
    
}
  • 위 코드는 Integer클래스 안에 있는 IngerCache 정적 클래스입니다.
  • 코드로 보았을 때 low = -128, high = 127로 Integer Cache의 기본 범위는 -128 ~ 127이라는 것을 알 수 있습니다.
  • 이 범위의 Integer 객체를 캐싱하는 이유는 개발할 때 빈번하게 사용되기 때문이라고 합니다. 
    • 자주 사용되는 값들만 따로 캐싱해 주는 것 같습니다.

Integer 클래스 내부의 valueOf 메서드

    @HotSpotIntrinsicCandidate
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
  • 위 코드는 Integer 클래스 내부에 있는 valueOf 메서드입니다.
  • 코드를 보니 입력된 매개변수 값이 IntegerCache 범위를 벗어나지 않으면 캐싱해둔 Integer 값을 사용하고, 아니면 새로운 Integer 클래스를 만들어서 반환하는 것을 알 수 있습니다.

Integer값 비교

다음 코드의 결괏값이 어떻게 될지 생각해 봅시다.

public class Main {
    public static void main(String[] args) {
        Integer firstNumber = -128;
        Integer secondNumber = -128;

        System.out.println(firstNumber == secondNumber); //동일성
        System.out.println(firstNumber.equals(secondNumber)); //동등성
    }
}

 

결론부터 말하지면 동일성 & 동등성 결과 모두 true를 반환합니다.

why?

  • Java는 모든 primitie type에 대해서 wrapper class를 제공하며, auto-boxing, auto-unboxing을 지원합니다.
  • Integer는 int형의 wrapper 클래스로 auto-boxing이 적용됩니다.
  • -128은 IntegerCache의 범위에 들어감으로 캐싱해둔 값을 반환하기 때문에 둘이 같은 Object를 가리키게 됩니다.
  • 그러므로 동일성 비교(==)를 해도 true를 반환한 것입니다.

동일성과 동등성

  • 동일성(==): 두 개의 오브젝트가 완전히 같을 경우 (주소 값 까지 같은 경우)
  • 동등성(equals): 두 오브젝트가 같은 정보를 같고 있는 경우

그렇다면 다음 코드의 결과값은 어떻게 될지 생각해 봅시다.

public class Main {
    public static void main(String[] args) {
        Integer firstNumber = 128;
        Integer secondNumber = 128;

        System.out.println(firstNumber == secondNumber); //동일성
        System.out.println(firstNumber.equals(secondNumber)); //동등성
    }
}
  • 128은 IntegerCache의 범위에 들어가지 않습니다. 그러므로 new Integer()를 통해 새로운 Object를 생성하게 됩니다.
    • 즉, 같은 Object를 가리키지 않게 됩니다.(주소 값이 달라지게 됩니다.)
  • 그러므로 동일성 비교(==)는 false, 동등성(equals) 비교는 true를 반환하게 됩니다.

정리

  • 자주 사용하는 객체를 캐싱하여 이미 있는 객체가 다시 생성되지 않도록 할 수 있습니다.
  • 자주 사용되는 객체를 캐싱하게 되면 메모리 요구량과 GC 비용을 줄일 수 있습니다.
  • 하지만 동일성(==) 비교시 의도치 않은 결과를 발생시킬 수 있습니다.

'Java' 카테고리의 다른 글

Enum 클래스 파헤치기  (0) 2021.02.23
[Java] 불변 리스트(Immutable ArrayList)  (0) 2021.02.15
Arrays.asList()  (0) 2021.02.15
Java Virtual Machine(JVM)  (0) 2021.02.13
가비지 컬렉션(Garbage Collection)  (0) 2021.02.13