[Java] 아무 생각 없이 생성했는데 동일한 객체?
Integer.valueOf(127) == Integer.valueOf(127)은 true?
Integer.valueOf(127) == Integer.valueOf(127)은 true가 출력된다고 한다.
뭔가 이상하다. 배운대로라면 Integer는 클래스, 즉 참조 타입이기 때문에 다른 객체가 생성되어 false가 떠야 맞는 것 같은데 그렇지가 않다.
그럼 다른 객체로 생성이 안된다는 얘기일까? 한 번 살펴보자.
다음 코드의 실행 결과를 한 번 예상해보자.
public class IntegerCacheCheck {
public static void main(String[] args) {
System.out.println(Integer.valueOf(128) == Integer.valueOf(128));
System.out.println(Integer.valueOf(127) == Integer.valueOf(127));
}
}
위 코드는 다음과 같은 결과가 출력된다.
false
true
128은 false가 출력되고 127은 true가 출력된다. 같은 Integer형인데 왜 이렇게 서로 다른 결과가 출력되는 것일까
Integer.valueOf()
위 결과의 이유를 알기 위해선 Integer 내부의 valueOf(int) 메서드를 이해해야한다.
먼저 Integer 클래스의 valueOf(int) 메서드를 보면 다음과 같이 구현되어 있다.
@HotSpotIntrinsicCandidate
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
여기서 IntegerCache.low는 -128, IntegerCache.high는 127이다. (이는 Integer 클래스의 내부 static 클래스인 IntegerCache에 선언되어 있다)
-128과 127 사이에 있는 수는 IntegerCache의 cache에서 꺼내오고 그 외의 범위는 새로운 객체를 생성하여 반환시킨다는 것을 확인할 수 있다.
IntegerCache
정확히 무슨 말 일까?
Integer는 내부에 Integer 객체를 미리 캐싱해두는 IntegerCache라는 static 클래스를 선언하고 있다.
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 {
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;
// Load IntegerCache.archivedCache from archive, if possible
VM.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 k = 0; k < c.length; k++)
c[k] = new Integer(j++);
archivedCache = c;
}
cache = archivedCache;
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
즉, -128과 127사이의 값은 Integer에서 미리 캐싱해두고 해당 범위의 값은 캐싱된 값을 반환시키는 것이다. 그래서 해당 범위의 값을 비교하면 같은 참조를 갖게 되므로 true가 나오는 것이다.
-128과 127사이의 값은 자주 사용되기 때문에 메모리를 절약하고자 캐싱을 해두고 있는 것이다.
이는 다른 관점에서 보면 플라이웨이트 패턴(Flyweight Pattern)이 적용된 것이라고 생각할 수 있다.
또, IntegerCache의 static block에서 값을 메모리에 초기화 하고 있으므로, 캐시는 클래스가 메모리로 로딩될 때 초기화된다.
캐싱은 Byte, Short, Long, Character 클래스에도 적용되어 있으며 그 범위는 다음과 같다.
- Byte, Short, Long: -127 ~ 127
- Integer: -128 ~ 127
- Character: 0 ~ 127
'Language > Java' 카테고리의 다른 글
[Java] JVM (0) | 2023.03.20 |
---|---|
[Java] "" vs new String("") (2) | 2023.03.19 |
[Java] abstract class vs interface (0) | 2023.01.20 |
[Java] stream vs for (1) | 2023.01.17 |
[Java] 3. Iterator (0) | 2022.08.17 |
댓글
이 글 공유하기
다른 글
-
[Java] JVM
[Java] JVM
2023.03.20 -
[Java] "" vs new String("")
[Java] "" vs new String("")
2023.03.19 -
[Java] abstract class vs interface
[Java] abstract class vs interface
2023.01.20 -
[Java] stream vs for
[Java] stream vs for
2023.01.17