백엔드 면접 용어 사전
Java·객체지향압축 객체 헤더

Compact Object Headers

객체 헤더를 두 워드에서 한 워드로 줄여 힙 사용량과 캐시 지역성을 개선하는 JVM 옵션. Java 25에서 프로덕션 기능.

모든 자바 객체가 달고 다니는 머리말(헤더)을 절반으로 줄이는 JVM 기능. Java 24에서 실험 기능, Java 25에서 프로덕션 기능이 됐다.

객체 헤더란 무엇인가

힙에 놓인 자바 객체는 필드 값만 들어 있는 게 아니다. 앞쪽에 JVM이 쓰는 관리 정보가 붙는다.

[기존 — 64비트 JVM]
┌─────────────────┬─────────────────┬───────────────┐
│  mark word      │  class pointer  │  필드들...    │
│    8바이트      │    4바이트      │               │
└─────────────────┴─────────────────┴───────────────┘
        헤더 12바이트 (+ 8바이트 정렬 패딩 → 실제 16바이트)
  • mark word — 해시코드, GC 세대 나이, 락 상태(어떤 스레드가 이 객체의 모니터를 쥐고 있는지) 등
  • class pointer — 이 객체가 어떤 클래스인지 가리키는 포인터

무엇을 줄였나

두 정보를 재배치해 한 머신 워드(8바이트) 에 담는다.

[Compact Object Headers]
┌─────────────────┬───────────────┐
│  헤더 8바이트   │  필드들...    │
└─────────────────┴───────────────┘
java -XX:+UseCompactObjectHeaders -jar app.jar

왜 체감이 큰가 — 작은 객체가 압도적으로 많다

Integer, 짧은 문자열, 리스트 노드처럼 필드가 몇 개 안 되는 객체에서는 헤더가 전체 크기의 상당 부분을 차지한다. 필드 8바이트짜리 객체라면 기존엔 헤더 16 + 데이터 8 = 24바이트인데, 헤더가 8이 되면 16바이트다 — 3분의 1이 줄어든다.

효과는 힙 절약에서 끝나지 않는다.

객체가 작아진다

  • 같은 캐시 라인(CPU가 메모리에서 한 번에 읽어 오는 단위, 보통 64바이트)에
    • 더 많은 객체가 들어간다
  • 캐시 적중률 상승 → 메모리 접근 지연 감소
  • GC가 훑어야 할 총 바이트 수도 줄어 GC 시간 감소

이게 캐시 지역성(locality) 이 좋아진다는 말의 실제 내용이다.

어디에 쓰나

컨테이너 밀도를 높이려는 환경(같은 메모리로 파드를 더 많이)에서 특히 유효하다. 애플리케이션 코드를 한 줄도 안 고치고 플래그 하나로 얻는다.

배경

자바의 메모리 발자국을 줄이려는 Project Lilliput의 결과물이다.

얼마나 줄어드는지 계산해 보기

  • long 필드 하나짜리 객체 (64비트 JVM, 8바이트 정렬)

  • 기존: — 헤더 12B + 필드 8B = 20B → 정렬 패딩으로 24B

  • Compact: — 헤더 8B + 필드 8B = 16B → 그대로 16B

    • 약 33% 절감
# 켜기 (Java 25에서 프로덕션 기능)
java -XX:+UseCompactObjectHeaders -jar app.jar

# 실제 객체 크기를 확인하려면 JOL(Java Object Layout) 같은 도구를 쓴다

컬렉션처럼 작은 객체가 대량으로 존재하는 워크로드에서 체감이 크다. HashMap의 노드, ArrayList가 담은 박싱된 숫자, 짧은 문자열이 전부 여기 해당한다.

왜 헤더가 두 워드였나

  • mark word — 해시코드·GC 나이·락 상태를 담느라 8바이트가 필요했다 class pointer 어떤 클래스인지 가리키는 포인터 (압축해도 4바이트)

Project Lilliput은 이 둘을 한 워드에 재배치했다. 락 상태 표현 방식까지 함께 손봐야 했기 때문에 실험 기능으로 오래 머물렀다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 모던 자바·스프링 — 가상 스레드·JEP 491·리액티브·jOOQ·Boot 4