2025년 3월에 출시된 JDK 24는 24개의 JEP(JDK Enhancement Proposal)를 포함하는 대규모 릴리스다. 그중에서도 Compact Object Headers(JEP 450)는 JVM 성능에 근본적인 변화를 가져오는 기능이다. 64비트 아키텍처에서 객체 헤더 크기를 96~128비트에서 64비트로 줄여, 힙 메모리를 최대 25%까지 절약할 수 있다.

객체 헤더란 무엇인가

Java에서 모든 객체는 실제 필드 데이터 외에 객체 헤더(Object Header)를 가진다. HotSpot JVM의 기존 객체 헤더는 두 부분으로 구성된다.

기존 구조 (96~128비트)

// 기존 객체 헤더 구조 (64비트 JVM)
+--------------------------------------------------+
| Mark Word (64비트)                                |
| - 해시코드, GC 나이, 잠금 상태 등                   |
+--------------------------------------------------+
| Klass Pointer (32비트, 압축 시 / 64비트, 비압축 시) |
| - 객체의 클래스 메타데이터 포인터                    |
+--------------------------------------------------+
| 배열 길이 (배열 객체인 경우 추가 32비트)              |
+--------------------------------------------------+

// 최소 객체 크기 계산
// 헤더: 12바이트 (Mark 8 + Klass 4, 압축 포인터)
// 패딩: 4바이트 (8바이트 정렬)
// 최소 객체 크기: 16바이트

Compact Object Headers (64비트)

// JEP 450 - Compact Object Headers
+--------------------------------------------------+
| Compact Header (64비트)                           |
| - Mark Word + Klass Pointer 통합                  |
| - 해시코드, GC 나이, 잠금 상태, 클래스 포인터 압축   |
+--------------------------------------------------+

// 최소 객체 크기: 8바이트 (헤더만)
// 실제로는 8바이트 정렬로 인해 차이 발생

활성화 방법

JDK 24에서 Compact Object Headers는 실험적 기능으로 제공되며, 명시적으로 활성화해야 한다.

# Compact Object Headers 활성화
java -XX:+UnlockExperimentalVMOptions \
     -XX:+UseCompactObjectHeaders \
     -jar myapp.jar

# 현재 객체 헤더 크기 확인
java -XX:+UnlockExperimentalVMOptions \
     -XX:+UseCompactObjectHeaders \
     -XX:+PrintFieldLayout \
     MyClass

실제 메모리 절약 효과 측정

JOL(Java Object Layout) 도구를 사용하여 실제 메모리 절약 효과를 확인할 수 있다.

import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.info.GraphLayout;

public class MemoryAnalysis {
    
    // 간단한 도메인 객체
    static class User {
        private long id;
        private String name;
        private int age;
    }
    
    public static void main(String[] args) {
        User user = new User();
        
        // 단일 객체의 레이아웃 출력
        System.out.println(ClassLayout.parseInstance(user).toPrintable());
        
        // 기존 헤더: User 객체 = 32바이트
        // Mark(8) + Klass(4) + id(8) + name(4, ref) + age(4) + padding(4)
        
        // Compact 헤더: User 객체 = 24바이트
        // Header(8) + id(8) + name(4, ref) + age(4)
        // 25% 절약!
        
        // 대량 객체 테스트
        User[] users = new User[1_000_000];
        for (int i = 0; i < users.length; i++) {
            users[i] = new User();
        }
        
        System.out.println("Total footprint: " + 
            GraphLayout.parseInstance((Object) users).totalSize() + " bytes");
    }
}

Maven에 JOL 의존성 추가

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.17</version>
</dependency>

Java 24의 다른 핵심 기능들

양자 내성 암호화 (JEP 496, 497)

양자 컴퓨터 시대를 대비한 새로운 암호화 알고리즘이 도입되었다.

import java.security.KeyPairGenerator;
import javax.crypto.KEM;

// ML-KEM (Module-Lattice-Based Key Encapsulation)
// 양자 내성 키 캡슐화 메커니즘
KeyPairGenerator kpg = KeyPairGenerator.getInstance("ML-KEM");
kpg.initialize(java.security.spec.NamedParameterSpec.ML_KEM_768);
var keyPair = kpg.generateKeyPair();

// 키 캡슐화
KEM kem = KEM.getInstance("ML-KEM");
KEM.Encapsulator enc = kem.newEncapsulator(keyPair.getPublic());
KEM.Encapsulated encapsulated = enc.encapsulate();
byte[] sharedSecret = encapsulated.key().getEncoded();
byte[] ciphertext = encapsulated.encapsulation();

// 키 역캡슐화
KEM.Decapsulator dec = kem.newDecapsulator(keyPair.getPrivate());
byte[] recovered = dec.decapsulate(ciphertext).getEncoded();

생성자 내 사전 실행문 (JEP 482, Preview)

생성자에서 super() 또는 this() 호출 전에 문장을 실행할 수 있다.

// Java 24 이전: 생성자 첫 줄은 반드시 super() 또는 this()
class OldStyle extends Parent {
    OldStyle(String rawData) {
        super(validate(rawData)); // static 메서드로 우회해야 했음
    }
    private static String validate(String data) {
        if (data == null) throw new IllegalArgumentException();
        return data.trim();
    }
}

// Java 24 (Preview): 사전 실행문 허용
class NewStyle extends Parent {
    NewStyle(String rawData) {
        // super() 전에 유효성 검사 가능
        if (rawData == null) {
            throw new IllegalArgumentException("데이터가 null입니다");
        }
        String cleaned = rawData.trim();
        
        // 필드 초기화도 가능 (인스턴스 참조 불가)
        super(cleaned);
    }
}

모듈 임포트 선언 (JEP 494, Preview)

// 기존: 개별 패키지 임포트
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;

// Java 24 (Preview): 모듈 단위 임포트
import module java.base;  // java.base 모듈의 모든 공개 패키지
import module java.sql;   // java.sql 모듈의 모든 공개 패키지

public class ModuleImportDemo {
    public static void main(String[] args) {
        // List, Map, Collectors 등 별도 임포트 없이 사용
        List names = List.of("Alice", "Bob", "Charlie");
        Map map = names.stream()
            .collect(Collectors.toMap(String::length, s -> s, (a, b) -> a));
    }
}

원시 타입 패턴 매칭 (JEP 488, Preview)

// switch에서 원시 타입 패턴 매칭
static String classify(Object obj) {
    return switch (obj) {
        case int i when i > 0    -> "양의 정수: " + i;
        case int i when i < 0    -> "음의 정수: " + i;
        case int i               -> "영";
        case double d when d > 0 -> "양의 실수: " + d;
        case long l              -> "long: " + l;
        case String s            -> "문자열: " + s;
        default                  -> "기타: " + obj;
    };
}

// instanceof에서 원시 타입
Object value = getFromDB();
if (value instanceof int count) {
    System.out.println("정수 카운트: " + count);
} else if (value instanceof double ratio) {
    System.out.println("비율: " + ratio);
}

JDK 24 적용 시 주의사항

  • JDK 24는 비LTS 릴리스로 6개월간 프리미어 지원만 제공된다. 프로덕션에는 JDK 21 LTS 또는 차기 LTS(JDK 25)를 권장한다.
  • Compact Object Headers는 실험적 기능이므로 충분한 테스트 후 적용해야 한다.
  • Windows 32비트 x86 포트가 제거되었으므로 해당 환경 사용자는 주의가 필요하다.
  • Preview 기능은 --enable-preview 플래그를 추가해야 사용할 수 있다.