Java 26, final이 드디어 진짜 final이 됩니다

Published on

in

Java 26, final이 드디어 진짜 final이 됩니다

2026.03.18 GA 기준
JDK 26 / Non-LTS
10 JEPs

Java 26, final이 드디어 진짜 final이 됩니다

2026년 3월 18일, Java 26이 정식 출시됐습니다(General Availability, OpenJDK 공식 페이지). 비LTS 릴리스이지만 G1 GC 처리량 최대 15% 향상, ZGC+AOT 캐시 동시 사용, HTTP/3 네이티브 지원 등 실무에 바로 영향 미치는 변화가 가득합니다. 공식 JEP 문서를 직접 크롤링해 확인한 수치와 함께 정리했습니다.

10
JEPs
+15%
G1 GC 처리량(최대)
41%
Spring 앱 기동 단축(AOT)
2,535
총 픽스 이슈

final 필드, 사실 20년간 진짜 final이 아니었습니다

💡 공식 JEP 문서와 JDK 5 히스토리를 같이 놓고 보니, final 키워드의 실제 보장 범위가 생각보다 훨씬 좁았다는 게 보였습니다.

Java 26에서 가장 눈에 띄는 변화 중 하나는 JEP 500: Prepare to Make Final Mean Final입니다. 이름부터 솔직합니다. “final을 진짜 final로 만들 준비를 합니다”라는 뜻입니다.

놀랍게도 JDK 5부터 지금까지, final 필드는 언제든 변경할 수 있었습니다. java.lang.reflect.Field.setAccessible(true)Field.set()을 조합하면 됩니다. 공식 JEP 500 문서는 이 사실을 직접 코드로 보여줍니다.

class C {
final int x;
C() { x = 100; }
}
Field f = C.class.getDeclaredField("x");
f.setAccessible(true);
C obj = new C();
System.out.println(obj.x);  // 100
f.set(obj, 200);
System.out.println(obj.x);  // 200 — final인데 바뀝니다

(출처: OpenJDK JEP 500 공식 문서, openjdk.org/jeps/500)

이게 왜 생겼냐면, 직렬화(serialization) 라이브러리가 객체를 역직렬화할 때 final 필드에 값을 써야 했기 때문입니다. 기능을 위해 언어 보장을 희생한 셈이었는데, JEP 500이 이걸 수십 년 만에 정리하기 시작합니다.

⚠️ Java 26에서 당장 어떻게 달라지나요?

아직 예외를 던지지는 않습니다. 경고(WARNING)만 출력합니다. 기본 모드는 --illegal-final-field-mutation=warn. 향후 릴리스에서 deny가 기본값이 됩니다. 지금 당장 --illegal-final-field-mutation=deny 옵션으로 실행해서 경고 발생 지점을 미리 파악해 두는 것이 좋습니다.

JVM이 final 필드를 진짜로 불변으로 신뢰하면 무엇이 좋아질까요? 상수 폴딩(constant folding) 같은 최적화를 더 공격적으로 적용할 수 있습니다. JIT 컴파일러가 값을 한 번만 계산하고 재사용하는 범위가 늘어나는 것입니다. 단순한 언어 정책 변화가 아니라, 성능 최적화를 위한 기반 공사라는 게 핵심입니다.

▲ 목차로 돌아가기

G1 GC가 느리다는 말, Java 26 이후론 다시 봐야 합니다

💡 G1 GC의 write barrier 명령어 수를 공식 JEP에서 직접 확인하니, “G1은 Parallel보다 느리다”는 통념을 재검토해야 할 수치가 나왔습니다.

HotSpot JVM의 기본 GC는 G1입니다. 그런데 “처리량(throughput)을 극대화하려면 Parallel GC를 써야 한다”는 말이 오랫동안 통용됐습니다. Java 26의 JEP 522가 이 공식을 흔듭니다.

기존 G1의 write barrier는 x64 기준 약 50개 명령어였습니다. 애플리케이션 스레드가 객체 참조 필드에 값을 쓸 때마다 이 코드가 실행됩니다. JEP 522는 두 번째 카드 테이블을 도입해 GC 최적화 스레드와 애플리케이션 스레드의 동기화 부하를 줄이고, write barrier를 약 12개 명령어로 축소했습니다.

구분 Java 25 이전 Java 26 (JEP 522)
write barrier (x64) ~50 명령어 ~12 명령어
처리량 향상 (참조 수정 많은 앱) +5~15%
처리량 향상 (일반 앱) +최대 5%
추가 native 메모리 (1GB heap 기준) +2MB

(출처: OpenJDK JEP 522, openjdk.org/jeps/522)

write barrier가 50개에서 12개로 줄어든다는 건, 모든 객체 참조 쓰기 연산이 그만큼 가벼워진다는 의미입니다. 앱 코드가 obj.field = value를 1억 번 호출하는 워크로드라면, GC 코드 오버헤드가 단숨에 4분의 1 수준으로 내려갑니다.

GC 엔지니어 Thomas Schatzl의 JDK 26 GC 변경 분석(2026.02.26)에 따르면, 이 변경은 “G1을 Serial·Parallel과 거의 동등한 수준의 동기화 비용으로 끌어내리는 이정표”라고 명시됩니다. 향후 G1이 유일한 기본 GC로 자리잡는 것을 목표로 하는 JEP 523과도 연결됩니다. (출처: tschatzl.github.io)

▲ 목차로 돌아가기

ZGC와 AOT 캐시, 둘 다 쓰고 싶었는데 드디어 됩니다

💡 “ZGC로 레이턴시를 잡았더니 기동 속도가 느려졌다”는 문제는, 사실 구조적으로 ZGC와 AOT 캐시가 공존할 수 없었기 때문이었습니다. JEP 516이 이 교착 상태를 해결합니다.

JDK 24에서 도입된 AOT(Ahead-of-Time) 캐시는 Spring PetClinic 기준 약 21,000개 클래스를 미리 로드된 상태로 시작할 수 있게 해 기동 시간을 41% 단축합니다. (출처: OpenJDK JEP 516, openjdk.org/jeps/516)

문제는 AOT 캐시의 객체 저장 방식이 GC마다 달랐다는 점입니다. 기존 방식은 메모리 주소를 64비트 직접 주소로 기록했고, ZGC는 객체 참조에 메타데이터 비트를 인코딩하는 독자적인 방식을 씁니다. 둘이 맞지 않았던 것입니다. 결과적으로 레이턴시를 잡으려고 ZGC를 쓰면, AOT 캐시를 포기해야 했습니다.

Java 26 이전의 선택 강요

✅ ZGC 사용 → AOT 캐시 사용 불가 (기동 느림)
✅ AOT 캐시 사용 → ZGC 사용 불가 (GC 레이턴시 증가)

JEP 516은 객체 참조를 메모리 주소 대신 논리 인덱스(logical index)로 저장하는 GC 무관 포맷을 도입합니다. 이 형식으로 저장된 캐시는 JVM 시작 시 백그라운드 스레드가 순차적으로 메모리에 올리면서, 어떤 GC가 사용되든 정상 동작합니다.

단, 스트리밍 방식은 여분의 CPU 코어 1개가 필요합니다. 메모리에 직접 매핑하는 기존 방식과 달리, 백그라운드에서 객체를 하나씩 생성하는 작업이 추가되기 때문입니다. 싱글 코어 환경처럼 여분 코어가 없을 때는 오히려 불리합니다. 이 판단을 위해 JVM은 학습 실행 환경을 기준으로 포맷을 자동 선택하되, 명시적으로 지정하려면 -XX:+AOTStreamableObjects 옵션을 씁니다.

▲ 목차로 돌아가기

HTTP/3가 Java 표준 API에 들어왔습니다

JEP 517: HTTP/3 for the HTTP Client API — HTTP/3는 2022년 IETF에 의해 표준화됐지만, Java 표준 API는 HTTP/1.1과 HTTP/2만 지원했습니다. Java 26에서 마침내 공식 지원이 추가됩니다. 코드 변경은 최소화됩니다.

// HTTP/3 활성화 — 클라이언트 레벨
var client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_3)
.build();
// 또는 요청 레벨에서만 활성화
var request = HttpRequest.newBuilder(URI.create("https://example.com/"))
.version(HttpClient.Version.HTTP_3)
.GET().build();

(출처: OpenJDK JEP 517, openjdk.org/jeps/517)

막상 써보면 주의할 점이 있습니다. HTTP/3는 QUIC(UDP 기반) 위에서 동작하는데, 이미 HTTP/2 연결이 맺어진 상태에서는 HTTP/3로 업그레이드할 수 없습니다. 처음 연결부터 HTTP/3로 시작해야 합니다. JEP 517은 이 문제를 해결하기 위해 총 4가지 협상 전략을 제공합니다.

전략 동작 방식 주의점
기본 (요청 레벨) HTTP/3 시도 후 타임아웃 시 fallback 첫 요청 지연 발생 가능
기본 (클라이언트 레벨) HTTP/3와 HTTP/2를 병렬로 시도 연결 낭비 가능
ALT_SVC 모드 첫 요청은 HTTP/2, 이후 HTTP/3 첫 요청만 HTTP/2 사용
HTTP_3_URI_ONLY 무조건 HTTP/3만 사용, fallback 없음 서버 지원 확실할 때만 사용

HTTP/3는 현재 전체 웹사이트의 약 1/3에 배포돼 있습니다. (출처: w3techs.com 통계, JEP 517 인용) 서버가 HTTP/3를 지원한다면, 패킷 손실이 잦은 환경에서 head-of-line blocking 없이 더 안정적인 연결을 유지할 수 있습니다. 단, 이번 구현은 클라이언트 측만 지원하며 서버 사이드 HTTP/3 구현은 이 JEP의 범위에 포함되지 않습니다.

▲ 목차로 돌아가기

Applet API가 사라졌습니다 — 그런데 이게 좀 조심스럽습니다

JEP 504: Remove the Applet API — JDK 17에서 deprecated for removal로 지정됐던 Applet API가 Java 26에서 완전히 삭제됩니다. 대부분의 현대 애플리케이션엔 관계없는 이야기처럼 들리지만, 실제론 조심할 곳이 있습니다.

레거시 기업 시스템 중 일부는 Applet API 클래스를 직접 사용하지 않더라도, 해당 API에 의존하는 서드파티 라이브러리를 경유해 간접 의존하는 경우가 있습니다. 이런 경우 Java 26으로 업그레이드 시 컴파일 또는 런타임 오류가 발생합니다. 의존성 트리를 먼저 스캔하는 것이 안전합니다.

그 외 Java 26에서 주목할 나머지 JEPs

  • JEP 524: PEM Encodings (2nd Preview) — 암호화 키·인증서를 PEM 형식으로 인코딩/디코딩하는 표준 API. 보안 라이브러리 설정이 단순해집니다.
  • JEP 525: Structured Concurrency (6th Preview) — 가상 스레드 기반 멀티스레드 코드를 단일 작업 단위로 묶어 취소·종료를 안전하게 관리. AI·클라우드 네이티브 워크로드에 유리합니다.
  • JEP 526: Lazy Constants (2nd Preview) — 초기화 시점을 유연하게 조절할 수 있는 지연 상수 API. JVM이 이를 진짜 상수로 인식해 final과 동등한 성능 최적화를 적용합니다.
  • JEP 529: Vector API (11th Incubator) — SIMD 벡터 연산을 Java 코드로 직접 표현. AI 추론·데이터 분석 워크로드의 성능 개선에 사용됩니다. 11번째 인큐베이터라는 점에서 정식화까지 갈 길이 멀다는 것도 팩트입니다.
  • JEP 530: Primitive Types in Patterns (4th Preview)instanceof·switch에서 int·double 같은 기본 타입을 패턴으로 사용 가능. 코드 일관성이 높아집니다.

▲ 목차로 돌아가기

프로덕션에 Java 26을 올려야 할까요?

💡 릴리스 스케줄을 Oracle 공식 페이지에서 직접 확인하니, Java 26의 지원 기간이 생각보다 짧습니다. 이 부분은 실제 도입 여부를 결정할 때 결정적 변수가 됩니다.

Java 26은 Non-LTS(비장기지원) 릴리스입니다. Oracle 공식 발표 기준, Oracle JDK 26에 대한 업데이트는 2026년 9월 Java 27이 출시될 때까지만 제공됩니다. 약 6개월의 지원 기간입니다. (출처: Oracle Java 26 출시 공식 블로그, blogs.oracle.com)

프로덕션 안정성이 중요한 환경이라면 현재 LTS인 Java 25를 유지하는 것이 현실적입니다. Java 26의 핵심 기능들 — JEP 522의 G1 개선, JEP 516의 ZGC+AOT — 은 다음 LTS 버전에서 안정화되어 정식 포함될 가능성이 높습니다. 개발·CI 파이프라인에서 Java 26을 테스트 버전으로 먼저 경험하면서, 코드베이스의 final 필드 변경 패턴을 미리 점검하는 전략이 합리적입니다.

📌 Java 26 도입 전 체크리스트

  • --illegal-final-field-mutation=deny 옵션으로 실행해 경고 여부 확인
  • Applet API 간접 의존 라이브러리 여부 스캔 (예: jdeps 도구 활용)
  • ZGC 사용 중이라면 -XX:+AOTStreamableObjects 활성화 효과 측정
  • G1 GC 환경이라면 처리량 벤치마크를 Java 25 대비 직접 측정
  • -XX:ParallelRefProcEnabled 옵션 사용 중인 경우 코드 검토 (deprecated)

솔직히 말하면, Java 26에서 가장 중요한 변화는 새로운 기능 추가보다 언어 보장의 회복입니다. final이 진짜로 불변이 되면 JVM 최적화 폭이 넓어지고, G1이 Parallel 수준으로 빨라지면 기본 GC를 교체할 필요가 줄어듭니다. 이런 기반 변화가 쌓여야 다음 단계의 성능과 안정성이 가능합니다.

▲ 목차로 돌아가기

자주 묻는 질문

Q1. Java 26은 LTS인가요?
아닙니다. Java 26은 비LTS(Non-LTS) 릴리스입니다. Oracle JDK 기준 지원 기간은 2026년 9월 Java 27 출시 시까지입니다. 현재 LTS는 Java 21(2031년까지)과 Java 25입니다. 프로덕션 장기 운영 환경은 LTS 버전을 권장합니다. (출처: Oracle Java 26 공식 블로그)
Q2. G1 GC 처리량 개선이 모든 애플리케이션에 적용되나요?
JEP 522 기준, 객체 참조 필드를 빈번하게 수정하는 애플리케이션에서 5~15%, 일반적인 앱에서도 최대 5%의 처리량 향상이 관측됐습니다. 단, 이는 JEP 공식 문서의 실험 결과이며 실제 워크로드에 따라 달라질 수 있습니다. 직접 벤치마크를 돌려 확인하는 것이 가장 정확합니다. 추가로 1GB 힙 기준 약 2MB의 네이티브 메모리가 추가 사용됩니다.
Q3. JEP 500으로 인해 기존 코드가 바로 깨지나요?
Java 26에서는 예외가 아니라 경고(WARNING)만 출력됩니다. 기본 모드가 --illegal-final-field-mutation=warn이기 때문입니다. 미리 deny 모드로 테스트해 두지 않으면, 향후 릴리스에서 deny가 기본값이 될 때 예외가 발생합니다. 지금 대응하는 것이 훨씬 수월합니다.
Q4. HTTP/3 지원이 서버 사이드에도 적용되나요?
JEP 517의 범위는 클라이언트 사이드만입니다. Java의 HttpClient API로 HTTP/3 서버에 연결하는 기능이 추가된 것이지, Java로 HTTP/3 서버를 구축하는 기능은 이번 릴리스에 포함되지 않습니다. 서버 사이드 구현은 향후 과제로 명시돼 있습니다.
Q5. JavaFX가 다시 Oracle 공식 지원을 받나요?
맞습니다. Oracle이 Java 26에서 JavaFX 상업 지원을 재도입했습니다. JavaFX 26과 25가 JDK 26과 함께 제공되며, JavaFX 21, 17, 8 업데이트는 2026년 4월 중 예정입니다. JDK 8용 JavaFX 지원은 2028년 3월까지 3년 연장됩니다. Oracle Java Verified Portfolio의 일부로 제공됩니다. (출처: Oracle Java 26 공식 블로그)

▲ 목차로 돌아가기

마치며

Java 26은 화려한 신기능 릴리스보다는 “그동안 잘못됐던 것을 바로잡는” 릴리스에 가깝습니다. 20년 넘게 뒤틀려 있던 final의 의미를 되찾고, G1 GC의 고질적인 처리량 열세를 구조적으로 해결하고, ZGC와 AOT 캐시의 오랜 충돌을 끝냈습니다.

개인적으로는 JEP 500이 가장 인상적입니다. “final이 진짜 final이 아니었다”는 사실을 공식 문서가 이렇게 담담하게 기술하는 것이 오히려 신선했습니다. 프로덕션 즉시 도입보다는, 이번 6개월 동안 CI 환경에서 충분히 검증한 뒤 다음 LTS에서 이 변화들의 안정화된 형태를 만나는 것이 현실적인 경로라고 봅니다.

본 포스팅 참고 자료

  1. OpenJDK JDK 26 공식 기능 목록 — openjdk.org/projects/jdk/26/
  2. Oracle Java 26 공식 출시 블로그 — blogs.oracle.com/java/the-arrival-of-java-26
  3. JEP 500: Prepare to Make Final Mean Final — openjdk.org/jeps/500
  4. JEP 516: Ahead-of-Time Object Caching with Any GC — openjdk.org/jeps/516
  5. JEP 517: HTTP/3 for the HTTP Client API — openjdk.org/jeps/517
  6. JEP 522: G1 GC: Improve Throughput by Reducing Synchronization — openjdk.org/jeps/522
  7. JDK 26 G1/Parallel/Serial GC Changes (Thomas Schatzl) — tschatzl.github.io

※ 본 포스팅은 2026년 3월 19일 기준으로 작성됐습니다. Java 26 및 OpenJDK 관련 JEP 내용, 지원 정책, 수치는 Oracle 및 OpenJDK 공식 발표를 기준으로 작성했으나, 본 포스팅 작성 이후 서비스 정책·UI·기능이 변경될 수 있습니다. 최신 정보는 공식 채널에서 반드시 재확인하시기 바랍니다.

댓글 남기기


최신 글


아이테크 어른경제에서 더 알아보기

지금 구독하여 계속 읽고 전체 아카이브에 액세스하세요.

계속 읽기