본문 바로가기
language/java

객체 생성 비용 줄이기 (Object Creation Cost)

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

객체 생성 비용 줄이기 (Object Creation Cost)

면접에서 자주 나오는 질문이 있다.

객체를 많이 생성하면 성능이 나빠지나요?
 

많은 사람들이

new 많이 하면 메모리 많이 쓰니까 느리다
 

정도로 답한다.

반은 맞고 반은 틀리다.

실제로 현대 JVM은 객체 생성에 엄청난 최적화가 들어가 있다.

오히려 많은 경우

객체 생성 자체보다

GC

동기화

DB

네트워크

문자열 처리
 

가 더 큰 병목이 된다.

이번 글에서는

  • 객체 생성 비용이 실제로 발생하는 곳
  • JVM이 객체 생성을 얼마나 빠르게 처리하는지
  • 어떤 경우 객체 생성이 문제가 되는지
  • 실무에서 적용 가능한 최적화 방법

을 알아본다.


객체 생성은 생각보다 빠르다

예시

 
User user = new User();
 

대부분은

Heap 탐색

↓

빈 공간 찾기

↓

메모리 할당

↓

객체 생성
 

이라고 생각한다.


하지만 HotSpot JVM은 그렇지 않다.

대부분의 객체는

TLAB
(Thread Local Allocation Buffer)
 

에서 생성된다.


TLAB란?

각 스레드에게

Heap 일부를 미리 나눠주는 영역이다.

예를 들어

Heap

├── Thread1 전용 영역
├── Thread2 전용 영역
├── Thread3 전용 영역
 

객체 생성 시

 
new User()
 

실제로는

현재 포인터 증가

↓

객체 생성 완료
 

수준이다.


그림으로 보면

TLAB

[객체][객체][객체][빈공간...]

               ↑

         현재 포인터
 

객체 생성

포인터 이동

↓

끝
 

그래서 JVM에서 객체 생성은 생각보다 매우 빠르다.


왜 빠를까?

C 언어의 malloc 같은 경우

빈 공간 탐색

↓

메모리 조각화 확인

↓

할당
 

이 필요하다.


반면 JVM Young Generation은

연속된 메모리 공간
 

을 사용한다.


따라서

Pointer Bump Allocation
 

만 수행한다.


예시

현재 주소

1000
 

객체 크기

64 byte
 

생성 후

1064
 

로 이동

끝.


그래서

 
new User()
 

는 생각보다 싸다.


객체 생성보다 비싼 것

실무에서 훨씬 비싼 것들


String 연결

 
String result = "";

for(int i = 0; i < 10000; i++) {
    result += i;
}
 

매번

String 생성

↓

char[] 생성

↓

복사
 

발생


실제로는

 
StringBuilder
 

사용


동기화

 
synchronized(lock) {

}
 

객체 생성보다

락 획득 비용이 더 큰 경우가 많다.


DB 호출

 
repository.findById(id);
 

객체 생성

수 ns ~ 수십 ns
 

DB

수 ms
 

차이가 수만 배 이상 난다.


객체 생성이 문제되는 경우

그렇다고

new 많이 해도 괜찮다
 

는 아니다.


문제가 되는 경우가 있다.


초당 수백만 객체 생성

예시

 
while(true) {

    new User();

}
 

또는

 
stream
 

파이프라인에서

 
map()
 
 
filter()
 
 
collect()
 

중간 객체가 엄청 생성되는 경우


결과

Young GC 폭증
 

GC 압박 증가

예시

 
for(int i = 0; i < 10000000; i++) {

    User user = new User();
}
 

객체 대부분은

Young Generation
 

으로 들어간다.


Young 공간이 꽉 차면

Minor GC
 

발생


계속 반복되면

GC Time 증가
 

응답 시간 증가


대용량 로그 처리

예시

 
String log =
        "[" + time + "]"
        + userId
        + action;
 

수천만 건 생성 시

임시 String 객체 폭증


GC 증가


실무에서 꽤 자주 보인다.


Wrapper 객체 남발

예시

 
Long total = 0L;

for(long i = 0; i < 1000000; i++) {

    total += i;
}
 

내부적으로

Long 객체 생성
 

반복


실제로는

 
long total = 0;
 

사용


객체 재사용은 항상 좋을까?

많은 주니어가 하는 실수


 
User user = new User();

for(...) {

    user.setName(...);
}
 

객체를 재사용하면 좋아 보인다.


하지만


가독성 하락

 
user.clear();
user.setXXX();
user.setYYY();
 

상태 추적 어려움


Thread Safe 문제

싱글톤 객체

 
@Component
public class UserHolder {

    private User user;
}
 

멀티 스레드 환경

데이터 오염
 

발생


그래서

객체 재사용

>

객체 생성 감소
 

가 항상 정답은 아니다.


실무 최적화 우선순위

많은 개발자가

 
new User()
 

줄이기에 집착한다.


하지만 실제 성능 개선 순위는

1. DB 호출 감소

2. 네트워크 호출 감소

3. 캐시 적용

4. 문자열 처리 개선

5. GC 감소

6. 객체 생성 감소
 

정도다.


객체 생성 최적화는

보통 마지막 단계다.


객체 Pool은 언제 사용할까?

과거 Java에서는

 
Object Pool
 

을 많이 사용했다.


예시

 
Connection
 

생성 비용

매우 비쌈


그래서

 
HikariCP
 

같은 Pool 사용


반면

 
User
 

같은 일반 객체는


대부분

Pool 사용 안 함
 

JVM이 더 잘 처리한다.


StringBuilder도 객체 생성 최적화다

예시

 
String result = "";

for(int i = 0; i < 10000; i++) {

    result += i;
}
 

실제로는

 
StringBuilder sb =
        new StringBuilder();

for(int i = 0; i < 10000; i++) {

    sb.append(i);
}
 

중간 객체 감소


GC 감소


성능 향상


실무 예시

나쁜 코드

 
public List<UserDto> findUsers() {

    List<User> users =
            repository.findAll();

    return users.stream()
            .map(user -> new UserDto(
                    user.getId(),
                    user.getName()
            ))
            .collect(Collectors.toList());
}
 

이 자체는 문제없다.


하지만

100만 건

500만 건

1000만 건
 

이면?


DTO 객체

수백만 개 생성


메모리 압박

GC 증가


이런 경우는

 
Paging
 

또는

 
Cursor
 

기반 조회 고려


JVM이 이미 하는 최적화

많은 사람이 모르는 사실

JVM은 객체 생성도 제거한다.


예시

 
public int calculate() {

    Point p = new Point(1, 2);

    return p.x + p.y;
}
 

JIT 최적화 후

객체 생성 제거
 

가능


이것이

Escape Analysis
 

다.


다음 글에서 자세히 다룬다.


정리

객체 생성은 생각보다 비싸지 않다.

현대 JVM은

TLAB

Pointer Bump Allocation

JIT 최적화
 

덕분에 객체를 매우 빠르게 생성한다.

실무에서 진짜 문제는

DB

네트워크

GC

문자열 처리
 

인 경우가 훨씬 많다.

다만

초당 수백만 객체 생성

대용량 Stream 처리

불필요한 Wrapper 객체

문자열 남발
 

상황에서는 GC 압박으로 성능 문제가 발생할 수 있다.

따라서

객체 생성 자체를 두려워하지 말고

불필요한 객체 생성을 줄이고

GC를 이해하는 것
 

이 더 중요하다.

다음 글에서는 Escape Analysis 재방문을 다룬다.

JVM이 어떻게 객체 자체를 생성하지 않는 수준까지 최적화하는지, Scalar Replacement와 Lock Elision까지 포함해서 실제 JIT 관점에서 분석해본다.

반응형

댓글