본문 바로가기
language/java

Integer Cache

by 죄니안죄니 2026. 6. 16.
반응형

이번 글은 Java 면접에서 가장 유명한 함정 문제 중 하나다.

면접관은 종종 다음 코드를 보여준다.

 
Integer a = 100;
Integer b = 100;

System.out.println(a == b);
 

그리고 묻는다.

결과는 무엇일까요?

많은 사람들이

Wrapper 객체니까

new Integer()

↓

객체 두 개 생성

↓

false
 

라고 대답한다.

하지만 실제 결과는

 
true
 

이다.

왜 그럴까?


Integer Cache

자주 사용하는 Integer 객체를 미리 생성해 재사용하는 JVM 최적화 기능


먼저 Integer는 객체이다.

 
Integer number = 100;
 

우리는 이렇게 작성한다.


AutoBoxing 글에서 공부했듯이

컴파일러는

 
Integer number =
        Integer.valueOf(100);
 

로 변경한다.


여기서 중요한 것은

new Integer()가 아니라 Integer.valueOf()를 사용한다는 점이다.


Integer.valueOf() 내부

실제로 JDK 내부는 매우 단순하다.

(이해하기 쉽게 단순화한 코드)

 
public static Integer valueOf(int value) {

    if (value >= -128 && value <= 127) {
        return IntegerCache.cache[value + 128];
    }

    return new Integer(value);

}
 

즉,

-128 ~ 127

↓

미리 만들어 둔 객체 반환

-----------------------

그 외 숫자

↓

새 객체 생성
 

이다.


실제 동작

 
Integer a = 100;
Integer b = 100;
 

컴파일러는

 
Integer a = Integer.valueOf(100);
Integer b = Integer.valueOf(100);
 

로 변경한다.


그리고

Integer.valueOf(100)

↓

Cache 조회

↓

같은 객체 반환
 

이다.


메모리

Integer Cache

-------------------

100 객체

-------------------

      ▲
      │
      │

a      b
 

둘 다 같은 객체를 참조한다.


그래서

 
System.out.println(a == b);
 

결과는

 
true
 

이다.


이번에는 1000

 
Integer a = 1000;
Integer b = 1000;
 

컴파일러

 
Integer a = Integer.valueOf(1000);
Integer b = Integer.valueOf(1000);
 

하지만

1000

↓

Cache 없음

↓

new Integer()

↓

new Integer()
 

이다.


메모리

Heap

Integer(1000)

▲
│
a


Integer(1000)

▲
│
b
 

서로 다른 객체이다.


그래서

 
a == b
 

결과는

 
false
 

이다.


왜 -128 ~ 127일까?

JVM 개발자들은

통계를 보면

0

1

-1

10

100
 

같은 작은 숫자가 압도적으로 많이 사용된다는 것을 알았다.


그래서

애플리케이션 시작

↓

Integer Cache 생성

↓

-128 ~ 127 객체 미리 생성

↓

계속 재사용
 

하도록 만들었다.


그림으로 이해하기

애플리케이션 시작

Integer Cache

----------------------------------

-128

-127

...

0

1

2

...

127

----------------------------------
 

이미 생성되어 있다.


사용

 
Integer.valueOf(10);
 

Cache

↓

10 객체 반환
 

새 객체를 만들지 않는다.


new Integer()는 다르다.

 
Integer a = new Integer(100);
Integer b = new Integer(100);
 

항상

new

↓

Heap

↓

새 객체 생성
 

이다.


메모리

Heap

Integer(100)

▲
│
a


Integer(100)

▲
│
b
 

그래서

 
a == b
 

 
false
 

이다.


equals()는 항상 값 비교

 
Integer a = 1000;
Integer b = 1000;

System.out.println(a.equals(b));
 

결과

 
true
 

이다.


왜냐하면

Integer의 equals는

 
public boolean equals(Object obj) {

    return this.value == other.value;

}
 

처럼

값(value)을 비교하기 때문이다.


== 와 equals()

이 차이를 반드시 이해해야 한다.


==

참조 비교

같은 객체인가?
 

메모리

a ─────────┐

           ▼

       Integer(100)

           ▲

b ─────────┘

↓

true
 

a ───► Integer(1000)

b ───► Integer(1000)

↓

false
 

equals()

값 비교

100 == 100

↓

true

1000 == 1000

↓

true
 

Long도 Cache가 있다.

 
Long a = 100L;
Long b = 100L;
 

결과

 
true
 

Character도 있다.

 
Character a = 'A';
Character b = 'A';
 

결과

 
true
 

Boolean은 더 단순하다.

Boolean.TRUE

Boolean.FALSE
 

딱 두 개만 존재한다.


실무에서는 ==를 사용할까?

거의 사용하지 않는다.


잘못된 코드

 
Integer a = getValue();

if (a == 100) {

}
 

이 코드는

AutoUnboxing 때문에

 
if (a.intValue() == 100)
 

가 되어 의도와 다르게 동작하거나, a가 null이면 NullPointerException이 발생할 수 있다.


또 다른 예

 
Integer a = 1000;
Integer b = 1000;

if (a == b) {

}
 

결과

 
false
 

이다.


그래서 실무에서는

 
Objects.equals(a, b)
 

또는

 
a.equals(b)
 

를 사용한다.


JVM 옵션으로 Cache 범위 변경

흥미롭게도 Cache 범위는 확장할 수 있다.

-XX:AutoBoxCacheMax=1000
 

처럼 설정하면

-128 ~ 1000
 

까지 Cache를 사용할 수 있다.

하지만 일반적인 애플리케이션에서는 거의 사용하지 않는다.


AutoBoxing과 연결해서 다시 보면

 
Integer a = 100;
 

실제로는

AutoBoxing

↓

Integer.valueOf(100)

↓

Integer Cache 조회

↓

기존 객체 반환
 

이다.


그림으로 전체 정리

Integer a = 100;

↓

AutoBoxing

↓

Integer.valueOf(100)

↓

Integer Cache

↓

기존 객체 반환

↓

a
 

Integer a = 1000;

↓

AutoBoxing

↓

Integer.valueOf(1000)

↓

Cache 없음

↓

new Integer(1000)

↓

a
 

면접 포인트

Integer Cache란?

자주 사용하는 Integer 객체(-128 ~ 127)를 미리 생성하여 재사용하는 JVM 최적화 기능이다.

왜 존재하는가?

작은 숫자는 매우 자주 사용되므로 객체 생성을 줄이고 메모리 사용량과 GC 부담을 줄이기 위해서이다.

== 와 equals() 차이는?

  • == : 객체의 참조(주소) 비교
  • equals() : 객체의 값 비교

실무에서는?

Wrapper 객체(Integer, Long 등)는 ==보다 equals() 또는 Objects.equals()를 사용하는 것이 안전하다.


다음 글 추천

이 흐름에서는 StringBuilder vs StringBuffer가 가장 자연스럽다.

단순히

"StringBuilder가 빠르고 StringBuffer는 synchronized"

수준이 아니라,

  • 왜 String은 immutable인가?
  • + 연산은 실제로 어떻게 동작하는가?
  • 컴파일러가 언제 StringBuilder로 변환하는가?
  • 왜 반복문에서는 +를 피해야 하는가?

까지 연결해서 이해할 수 있다.

반응형

'language > java' 카테고리의 다른 글

BigDecimal을 사용하는 이유  (0) 2026.06.16
StringBuilder vs StringBuffer  (0) 2026.06.16
AutoBoxing / Unboxing  (0) 2026.06.16
Serialization / Deserialization  (0) 2026.06.16
JDK Proxy vs CGLIB  (0) 2026.06.16

댓글