백엔드 면접 용어 사전
Java·객체지향Project Leyden · AOT Cache

AOT 캐시

로드·링크가 끝난 클래스와 메서드 프로파일을 파일로 저장해 재사용하는 JVM 기동 최적화. 코드 변경 없이 최대 42% 단축.

JVM이 기동할 때마다 반복하던 준비 작업을 미리 해서 파일로 저장해 두는 기능. Project Leyden의 결과물이고 Java 24~25에 걸쳐 들어왔다.

JVM이 느리게 시작하는 이유

  • 클래스 파일 읽기 — .class 파일을 디스크에서 읽는다
  • 파싱·검증 — 바이트코드가 규칙에 맞는지 확인
  • 로드·링크 — 메모리에 클래스 구조를 만들고 심볼 참조를 실제 주소로 연결
  • 인터프리터 실행 — 일단 한 줄씩 해석하며 실행
  • 프로파일 수집 — 어느 메서드가 자주 불리는지 통계를 모은다
  • JIT 컴파일 — 그제서야 뜨거운 메서드를 기계어로 번역

문제는 ①~⑥이 매 기동마다 똑같이 반복된다는 것이다. 서버 한 대가 몇 달씩 사는 시절엔 상관없었지만, 컨테이너가 수시로 뜨고 죽는 환경에서는 이 준비 과정이 그대로 비용이다.

해법 — 결과를 저장해 둔다

JEP (Java)캐시에 담는 것
483 (24)로드·링크까지 끝난 클래스 — ①②③을 건너뛴다
514 (25)캐시 생성을 한 단계로 축약(원래는 학습 실행 + 생성 2단계)
515 (25)메서드 호출 프로파일 — ⑤를 건너뛰고 기동 직후 바로 ⑥
# 한 번 "학습 실행"을 시키면 캐시 파일이 만들어진다
java -XX:AOTCacheOutput=app.aot -jar app.jar

# 이후 기동에서 재사용
java -XX:AOTCache=app.aot -jar app.jar

측정된 기동 시간 단축은 최대 42% 수준이다.

GraalVM 네이티브 이미지와 무엇이 다른가

AOT 캐시네이티브 이미지
결과물평범한 JVM + 캐시 파일독립 실행 바이너리
기동수십 % 단축밀리초 단위
제약없음 — 코드 변경 0닫힌 세계 가정 — 런타임 리플렉션·동적 프록시·클래스 로딩을 빌드 때 다 알아야 하고, 힌트 설정과 긴 빌드 시간이 든다
최고 성능JIT 그대로 유지JIT이 없어 장기 실행 처리량은 손해 볼 수 있다

"닫힌 세계 가정"이란 프로그램이 실행 중에 새 클래스를 만들거나 불러오지 않는다고 전제한다는 뜻이다. 그래야 빌드 시점에 필요한 코드만 남기고 전부 잘라낼 수 있다.

"네이티브 이미지는 부담스럽고 기동은 줄이고 싶다" 는 대부분의 서버가 AOT 캐시에 해당한다. 둘은 대체재가 아니라 비용이 다른 두 선택지다.

면접 포인트

"기동 속도를 줄이는 방법은?"에 ① Spring lazy-initialization ② Spring Data AOT ③ AOT 캐시 ④ 네이티브 이미지 순으로 비용이 싼 것부터 나열하면 좋다.

Spring 애플리케이션에 적용해 보면

# 1) 학습 실행 — 실제 기동과 같은 경로를 한 번 돌린다
java -XX:AOTCacheOutput=app.aot -jar app.jar

# 2) 이후 기동은 캐시를 읽는다
java -XX:AOTCache=app.aot -jar app.jar

주의할 점이 있다. 학습 실행 때 지나간 코드 경로만 캐시에 담긴다. 기동만 하고 바로 종료한 학습 실행은 컨트롤러·리포지토리 초기화 경로를 못 담을 수 있어, 실제 트래픽과 비슷한 워밍업까지 시키는 편이 효과가 크다.

한 단계 워크플로의 비용

  • -Xms4g -Xmx4g 로 학습 실행하면 → 약 8GB 필요
  • (캐시 생성 과정이 힙을 한 벌 더 쓴다)

CI에서 캐시를 만들어 이미지에 함께 넣는 방식이 흔하다 — 다만 JDK 버전이나 클래스패스가 바뀌면 캐시가 무효가 되므로 빌드 파이프라인에 묶어 두는 게 안전하다.

함께 보면 좋은 용어

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