Integer.valueOf(127) == Integer.valueOf(127) 가 true 인 이유

Lily·2024년 4월 21일
5
post-thumbnail

들어가기 전

자바의 모든 type 은 아래 두 가지 범주에 속한다.

Primitive Types

값을 binary bits 형태로 직접 저장하는 8개의 primitive Types 이 있다. (byte, short, int, long, float, double, char and boolean)
예를 들어 int a = 5; int b = 5; 를 비교할 때 a 와 b 는 5 라는 이진 값을 직접 보유하고 있기 때문에 a == b 를 사용하면 실제로 5 == 5 를 비교하여 true 를 반환한다.

Reference Types

Primitive Types 이외의 모든 type은 Reference Types (예: 클래스, 인터페이스, 열거형, 배열 등)의 범주에 속한다. Reference Types 의 변수는 컴퓨터의 메모리에 객체의 위치를 저장한다. 이러한 변수는 객체를 참조한다.
예를 들어 Integer a = new Integer(5); Integer b = new Integer(5) 에서 a와 b는 5라는 이진 값을 보유하는 것이 아니라 두 객체 모두 5라는 값을 포함하는 두 개의 개별 객체의 메모리 주소를 보유한다.
따라서 a == b를 사용하여 a와 b를 비교하려고 하면 실제로는 두 개의 개별 메모리 주소를 비교하는 것이므로 false 이고, a와 b에 대해 실제 값이 같음을 비교하려면 a.equals(b)를 수행해야 한다.

Integer 비교

auto-boxing 과 auto-unboxing

1) auto-boxing 의 예

Integer c = 128;

Java 컴파일러는 위 코드를 Integer c = Integer.valueOf(128) 로 autoBoxing 을 적용한다.

autoBoxing 이란 -

primitive Types 의 값을 wrapper 클래스의 객체로 바꾸는 과정을 의미한다.

Java 컴파일러는 아래의 경우에 autoBoxing 을 적용한다.

  • Passed as a parameter to a method that expects an object of the corresponding wrapper class.
  • Assigned to a variable of the corresponding wrapper class.

primitive Types 이 Wrapper 클래스의 타입의 파라미터를 받는 메서드를 통과할 때
primitive Types 이 Wrapper 클래스의 변수로 할당될 때

1) auto-unboxing 의 예

Integer c = 128;
int e = c;

Java 컴파일러는 위 코드를 int e = c.intValue(); 로 autoUnboxing 을 적용한다.

autoUnboxing 이란 -

Wrapper 클래스 타입을 primitive Types 으로 변환하는 과정을 의미한다.

Java 컴파일러는 아래의 경우에 autoUnboxing 을 적용한다.

  • Passed as a parameter to a method that expects a value of the corresponding primitive type.
  • Assigned to a variable of the corresponding primitive type.

Wrapper 클래스 타입이 primitive Types 의 파라미터를 받는 메서드를 통과할 때
Wrapper 클래스 타입이 primitive Types 의 변수로 할당될 때

Integer.valueOf(128) == Integer.valueOf(128) 은 false

Integer a = 128;
Integer b = 128;

System.out.println(a == b); // Output -- false

이제 두 개의 Integer 객체 a와 b 를 생성하고 == 을 사용하여 비교하려고 하면 두 참조가 서로 다른 객체를 보유하고 있기 때문에 false가 반환된다.

Integer.valueOf(127) == Integer.valueOf(127) 은 true

Integer a = 127;
Integer b = 127;
System.out.println(a == b); // Output -- true

그런데 값 127을 a와 b에 모두 할당하고 == 을 사용하여 비교하면 true 가 반환된다.
위 코드에서 분명 a와 b에 서로 다른 객체를 할당하고 있고, true 가 반환되는 때는 a와 b가 모두 동일한 객체를 가리키는 경우이다.

어떻게 true 가 반환이 되었을까?

일단 Integer a = 127;Integer a = Integer.valueOf(127); 로 autoBoxing 된다.
따라서 Integer 객체를 반환하는 것은 Integer.valueOf() 메서드이며, 이는 이 메서드가 내부적으로 무언가를 수행하고 있다는 것을 의미한다.

/**
 * Returns an {@code Integer} instance representing the specified
 * {@code int} value.  If a new {@code Integer} instance is not
 * required, this method should generally be used in preference to
 * the constructor {@link #Integer(int)}, as this method is likely
 * to yield significantly better space and time performance by
 * caching frequently requested values.
 *
 * This method will always cache values in the range -128 to 127,
 * inclusive, and may cache other values outside of this range.
 *
 * @param  i an {@code int} value.
 * @return an {@code Integer} instance representing {@code i}.
 * @since  1.5
 */
 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() 메서드의 소스 코드를 살펴보면,

전달된 int iIntegerCache.low 보다 크고 IntegerCache.high 보다 작으면 IntegerCache 에서 Integer 객체를 반환한다는 것을 알 수 있다.
IntegerCache.lowIntegerCache.high 의 기본값은 각각 -128과 127 이다.

즉, 새로운 Integer 객체를 생성하여 반환하는 대신 Integer.valueOf() 는 전달된 int i 가 -128 보다 크고 127 보다 작은 경우 내부 IntegerCache에서 Integer 객체를 반환한다.

Java는 -128 에서 127 범위에 속하는 Integer 객체를 캐시하는데, 이는 일상적인 프로그래밍에서 이 Integer 범위가 많이 사용되기 때문에 간접적으로 메모리를 절약할 수 있기 때문이다.

IntegerCache

아래 코드에서 Integer 클래스는 -128 에서 127 까지의 Integer 객체를 보관하는 캐시 역할을 하는 내부 static IntegerCache 클래스를 유지하므로 127 의 Integer 객체를 가져오려고 할 때 항상 동일한 객체를 얻을 수 있다.


    /**
     * 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 {@code -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
     * jdk.internal.misc.VM class.
     *
     * WARNING: The cache is archived with CDS and reloaded from the shared
     * archive at runtime. The archived cache (Integer[]) and Integer objects
     * reside in the closed archive heap regions. Care should be taken when
     * changing the implementation and the cache array should not be assigned
     * with new Integer object(s) after initialization.
     */

    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer[] cache;
        static Integer[] archivedCache;

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    h = Math.max(parseInt(integerCacheHighPropValue), 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(h, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            // Load IntegerCache.archivedCache from archive, if possible
            CDS.initializeFromArchive(IntegerCache.class);
            int size = (high - low) + 1;

            // Use the archived cache if it exists and is large enough
            if (archivedCache == null || size > archivedCache.length) {
                Integer[] c = new Integer[size];
                int j = low;
                for(int i = 0; i < c.length; i++) {
                    c[i] = new Integer(j++);
                }
                archivedCache = c;
            }
            cache = archivedCache;
            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }

캐시는 static block 으로 인해 클래스가 메모리에 로드될 때 초기화된다.
캐시의 최대 범위는 -XX:AutoBoxCacheMax JVM 옵션으로 제어할 수 있다.

이 캐싱은 Integer 객체에만 적용되는 것이 아니고 Integer.IntegerCache 와 마찬가지로 각각 Byte, Short, Long, Character에 대한 ByteCache, ShortCache, LongCache, CharacterCache 도 있다.

Byte, Short, Long 의 캐싱 범위는 -127에서 127 까지로 고정되어 있지만, Character 의 경우 0에서 127 까지로 범위가 정해져 있다.
범위는 Integer 만 수정할 수 있다.

Reference

Section 3.3 Primitive Types vs. Reference Types
Autoboxing and Unboxing
Java Integer Cache - Why Integer.valueOf(127) == Integer.valueOf(127) Is True

profile
내가 하고 싶은 거

0개의 댓글

관련 채용 정보