Java 26, 출시 6일 만에 확인한 5가지

Published on

in

Java 26, 출시 6일 만에 확인한 5가지
Java 26 기준
2026.03.17 GA
JDK 26 build 26+35

Java 26, 출시 6일 만에 확인한 5가지

10개 JEP, 빠른 성능 개선, 그리고 지금 바로 CI 파이프라인을 손봐야 하는 이유까지

10개
JEP 포함
5~15%
G1 GC 처리량 개선
6개월
지원 종료까지
2,535건
JIRA 이슈 수정

2026년 3월 17일, Java 26이 공식 출시됐습니다. 6개월 릴리스 주기로 따지면 17번째 정시 출시입니다. 이번 릴리스에 포함된 JEP는 10개로 최근 버전들에 비해 숫자가 적은 편입니다. 그런데 그게 오히려 이 릴리스를 더 흥미롭게 만듭니다. JEP 수가 적다는 건 대형 변화를 위한 정지 작업이 진행 중이라는 신호이기 때문입니다. G1 GC 처리량 개선, HTTP/3 표준 라이브러리 편입, 그리고 지금 당장 프로젝트 CI 로그를 확인해야 하는 이유까지 — 공식 문서를 직접 뜯어서 정리했습니다.

G1 GC 성능이 좋아졌는데 코드를 건드릴 필요가 없습니다

JEP 522 핵심 — 듀얼 카드 테이블

JDK 9 이후 기본 GC인 G1은 애플리케이션 스레드와 GC 스레드가 CPU를 나눠 씁니다. 문제는 두 스레드가 같은 카드 테이블(card table, 객체 참조가 바뀔 때마다 업데이트되는 자료구조)을 공유해서 쓰다 보니, 동기화 오버헤드가 처리량과 지연 시간을 동시에 갉아먹는다는 점이었습니다.

Java 26(JEP 522)은 카드 테이블을 하나 더 추가하는 방식으로 이걸 해결합니다. 애플리케이션 스레드는 첫 번째 테이블에 동기화 없이 자유롭게 쓰고, G1 옵티마이저 스레드는 두 번째 테이블을 따로 처리합니다. G1이 pause 목표를 초과할 것 같다고 판단하면 두 테이블을 원자적으로 교체(swap)합니다. (출처: Oracle JDK 26 Release Notes, 2026.03.17)

실제로 어느 정도 빨라지나요

객체 참조를 빈번하게 수정하는 워크로드에서 5~15% 처리량 향상이 관측됐습니다. (출처: openjdk.org JEP 522) 참조 변경이 적은 워크로드에서도 x64 아키텍처 기준 최대 5%의 추가 개선이 나타났습니다. 무엇보다 두 번째 카드 테이블의 메모리 비용은 힙 1GB당 약 2MB인데, Java 20 이전에 G1이 같은 용도로 소비하던 메모리의 8분의 1 수준입니다. 성능이 올라가면서 메모리는 오히려 덜 씁니다.

Spring Boot 서버라면 HTTP 요청마다 객체가 대량으로 생성·참조됩니다. JDK 버전만 올려도 이 개선이 자동으로 적용됩니다. 코드를 건드릴 필요가 없습니다.

💡 G1이 기본 GC인데 왜 이걸 지금 바꾸는지 궁금해서 확인해 봤습니다.
사실 G1의 단일 카드 테이블 구조는 설계 초기부터 알려진 병목이었습니다. 하지만 대안 설계가 ZGC나 Shenandoah처럼 완전히 다른 GC로 이어졌고, G1 자체를 수술하는 건 위험 부담이 컸습니다. JEP 522는 그 수술을 드디어 마무리한 변화입니다. G1을 쓰는 애플리케이션 — 사실상 대부분의 Java 서버 — 이 바로 이 혜택을 받습니다.

HTTP/3이 JDK 안으로 들어왔습니다 — 코드 한 줄로 설정

JEP 517 — HttpClient API에 HTTP/3 추가

전체 웹 트래픽의 약 3분의 1이 이미 HTTP/3를 사용하고 있습니다. (출처: wikidocs.net/blog/@jaehong/9440) 주요 브라우저는 진작에 지원을 마쳤고, Java의 HttpClient API는 Java 26(JEP 517)에서 드디어 HTTP/3을 공식 지원합니다. 별도 QUIC 라이브러리를 추가할 필요 없이, 아래처럼 버전 설정 한 줄이면 됩니다.

HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_3)
.build();

서버가 HTTP/3을 지원하지 않으면 자동으로 HTTP/2로 폴백합니다. (출처: Oracle JDK 26 Release Notes, 2026.03.17)

Spring Boot에서는 어디까지 적용되나요

Spring Boot에서 RestClient가 JDK HttpClient를 백엔드로 쓰고 있다면 클라이언트 레벨에서 이 설정이 적용됩니다. 반면 WebFlux의 기본인 WebClient + Reactor Netty 조합은 해당되지 않습니다. Netty는 자체적인 HTTP/3 지원 로드맵을 갖고 있습니다. 실제로 효과가 큰 곳은 고동시성 외부 API 호출, AI 추론 엔드포인트 연동, 결제 게이트웨이처럼 요청 하나가 느려지면 전체가 밀리는 상황입니다. HTTP/3의 핵심 이점인 헤드오브라인 블로킹 제거 덕분입니다.

HTTP/3 협상 전략 4가지

HTTP/3는 TCP가 아닌 UDP 기반 QUIC 위에서 돌아가서 기존 연결을 “업그레이드”할 수 없습니다. Java 팀은 이 문제를 4가지 협상 전략으로 풀었습니다. HTTP/3을 먼저 시도하고 타임아웃 시 폴백하는 방식, 두 버전을 동시에 시도하는 레이싱 방식, HTTP/2로 먼저 붙고 서버가 HTTP/3 지원을 알려주면 전환하는 발견 방식, 그리고 HTTP/3만 강제하는 방식입니다. 각각에 타임아웃 지연·자원 낭비·초기 라운드트립·서버 호환성이라는 트레이드오프가 존재합니다.

final이 진짜 final이 되고 있습니다 — 내 코드가 아닐 수 있습니다

JEP 500 — 리플렉션으로 final 필드를 바꿀 때 경고 발생

Java의 final 필드는 이름과 달리 리플렉션으로 언제든 바꿀 수 있었습니다. Field.setAccessible(true)Field.set()을 호출하면 final이라도 가변 필드처럼 동작했습니다. Java 26(JEP 500)부터는 이렇게 하면 런타임 경고가 발생합니다.

WARNING: Final field SomeClass.FINAL_VALUE has been mutated reflectively

지금은 경고지만, 향후 버전에서 기본적으로 예외를 던지게 됩니다. 오라클 공식 문서에서 타임라인을 별도로 공개하지 않았습니다. (출처: Oracle JDK 26 Release Notes, 2026.03.17)

💡 공식 발표문과 실제 사용 흐름을 같이 놓고 보니 이런 차이가 보였습니다.
이 경고가 내 코드에서 나올 가능성보다 서드파티 라이브러리에서 나올 가능성이 훨씬 높습니다. Hibernate 일부 구성, Mockito spy, Lombok, 직렬화 프레임워크들이 오랫동안 이 방식을 써왔습니다. Spring Framework 7.0은 이미 리플렉션 사용을 정리했지만, Hibernate 7.x 이전 버전과 테스트 프레임워크는 확인이 필요합니다. Java 26을 CI 매트릭스에 추가해서 의존성 로그를 먼저 확인하는 것이 안전합니다.

CI에서 바로 확인하는 두 가지 플래그

경고를 지금 당장 오류로 처리해서 문제 소스를 CI에서 잡아내거나, 라이브러리 업데이트를 기다리는 동안 일시적으로 억제할 수 있습니다. (출처: dev.to/paszekdev, 2026.03.17 기준)

# CI에서 지금 바로 오류로 잡기
--illegal-final-field-mutation=deny
# 라이브러리 업데이트 전 임시 억제
--enable-final-field-mutation=ALL-UNNAMED

AOT 캐시가 ZGC에서도 됩니다 — 무엇이 달라지는가

JEP 516 — GC에 상관없이 AOT 캐시 사용

Java 24에서 도입된 AOT(Ahead-of-Time) 캐시는 JVM 시작 시간과 워밍업 속도를 크게 단축시키는 기능입니다. 그런데 문제가 있었습니다. 캐시된 Java 객체가 GC별 고유 형식으로 저장되기 때문에, G1으로 훈련한 캐시를 ZGC에서 재사용할 수 없었습니다. 저지연 특성 때문에 ZGC를 써야 하는 실시간 시스템은 AOT 캐시를 포기해야 했습니다.

JEP 516은 메모리 주소 대신 논리적 인덱스를 저장하고, 로드 시 실제 주소로 변환하는 방식을 도입했습니다. ZGC의 고급 메모리 레이아웃과 AOT 캐시가 충돌하지 않게 됐습니다. (출처: Oracle JDK 26 Release Notes, 2026.03.17) tail latency에 민감한 웹 서버, 실시간 시스템이라면 ZGC + AOT 캐시 조합을 이제 함께 쓸 수 있습니다.

주목할 만한 소소한 추가 사항 — UUIDv7

JEP는 아니지만 실무에서 바로 쓸 수 있는 게 있습니다. UUID.randomUUID()는 버전 4 UUID를 생성하는데, 완전한 랜덤값이라 데이터베이스 기본 키로 쓰면 B-트리 인덱스 단편화가 심해집니다. Java 26은 UUID.ofEpochMillis(long timestamp)를 추가했습니다. 앞 비트에 밀리초 타임스탬프가 들어가서 새로운 행이 항상 인덱스 끝에 삽입됩니다. 별도 라이브러리 없이 메서드 하나만 바꾸면 됩니다. (출처: dev.to/paszekdev, 2026.03.17 기준)

항목 Java 25 이전 Java 26
G1 GC 카드 테이블 단일 (동기화 오버헤드) 듀얼 (5~15% 처리량 개선)
HTTP/3 지원 외부 라이브러리 필요 JDK 내장
AOT 캐시 + ZGC 사용 불가 ✅ 사용 가능
final 필드 리플렉션 변경 경고 없음 런타임 경고 발생
시간순 UUID 생성 외부 라이브러리 필요 UUID.ofEpochMillis()

표 기준: Oracle JDK 26 Release Notes (2026.03.17), JEP 문서 원문 대조

Java 26을 프로덕션에 올리면 안 되는 이유

6개월짜리 지원 — 2026년 9월이면 끝납니다

Java 26은 non-LTS(비장기 지원) 릴리스입니다. 오라클 공식 발표에 따르면 Java 27이 출시되는 2026년 9월에 Premier Support가 종료됩니다. (출처: Oracle 공식 블로그 “The Arrival of Java 26”, 2026.03.17) 6개월 안에 Java 27로 갈아타야 한다는 뜻입니다. 보안 패치를 오래 받으면서 안정적으로 운영해야 하는 서버라면 LTS인 Java 25가 올바른 선택입니다.

⚠️ Spring Boot 4.0.x는 Java 25까지 공식 지원입니다. Java 26은 “best effort” 수준으로, 동작은 하지만 공식 보장이 없습니다. 프로덕션 배포 환경에서는 Java 25 LTS를 유지하는 것이 안전합니다. (출처: dev.to/paszekdev, 2026.03.17 기준)

Java 26의 올바른 사용법 — CI 매트릭스

Java 26을 지금 당장 활용할 수 있는 실질적인 방법이 있습니다. 프로덕션 배포가 아닌 CI 파이프라인에 Java 26을 추가하는 겁니다. JEP 500 경고가 어느 의존성에서 나오는지 지금 미리 파악해두면, 향후 LTS 버전에서 예외가 터지기 전에 여유 있게 대응할 수 있습니다. Java 21에서 올라올 계획이라면, Java 25에 이미 들어온 Compact Object Headers(객체 헤더 12~16바이트 → 8바이트, 힙 사용량 약 22% 감소)와 이번 Java 26의 G1 GC 개선을 합산하면 마이그레이션 명분이 충분해집니다.

Java 26이 실제로 준비하고 있는 것

Vector API 11차 인큐베이터가 말해주는 것

Vector API는 이번에도 인큐베이터 단계를 유지했습니다. 열한 번째입니다. 이 API 자체는 사실 구현이 다 됐습니다. 이 API가 정식화되려면 Project Valhalla의 Value Classes가 먼저 나와야 하고, 그 전에 JEP 500이 final 필드의 진짜 불변성을 보장해야 합니다. 두 조각이 Java 26에 같이 들어왔다는 건, 공식 문서가 명시적으로 말하지 않더라도, Valhalla의 첫 프리뷰가 멀지 않았다는 신호로 읽을 수 있습니다.

Structured Concurrency 6차 프리뷰 — 아직 쓰면 안 됩니다

Structured Concurrency(JEP 525)는 Java 19부터 프리뷰를 반복하고 있습니다. Java 26에서는 Joiner 인터페이스에 onTimeout()이 추가됐고, allSuccessfulOrThrow()가 스트림 대신 리스트를 반환하게 바뀌었습니다. 프리뷰 API는 다음 버전에서 또 바뀔 수 있습니다. 프로덕션 코드에 쓰면 버전 업그레이드마다 수정해야 합니다.

💡 Java가 async/await 대신 가상 스레드를 선택한 이유를 JEP 문서와 실제 API 설계를 나란히 놓고 보니 더 선명하게 보였습니다.
async/await를 쓰는 언어들은 비동기 함수가 호출 체인 전체로 전파되면서 동기·비동기 API가 이중화되는 문제(colored function)를 겪습니다. Java는 가상 스레드를 통해 기존 동기 코드 스타일을 그대로 유지하면서 높은 동시성을 확보하는 길을 선택했습니다. Structured Concurrency는 그 위에서 스레드 생명주기를 명확하게 관리하는 층입니다. 단순한 편의 기능이 아니라 Java 동시성 모델의 철학적 방향입니다.

Thread.stop() 이 드디어 제거됐습니다

JDK 1.2(1998년)에 deprecated됐고, JDK 18에 removal deprecated된 Thread.stop()이 Java 26에서 완전히 삭제됐습니다. (출처: Oracle JDK 26 Release Notes, 2026.03.17) 오래된 코드베이스에서 이 메서드를 직접 호출하는 곳이 있다면 컴파일 자체가 안 됩니다. 구버전 클래스 파일을 Java 26에서 실행하면 UnsupportedOperationException 대신 NoSuchMethodError가 납니다.

Q&A

Q1. Java 26은 LTS인가요?
아닙니다. Java 26은 non-LTS 릴리스입니다. 2026년 9월 Java 27 출시와 함께 Premier Support가 종료됩니다. LTS는 Java 25입니다. 장기 운영 서버라면 Java 25를 유지하고, Java 26은 CI 파이프라인 검증용으로 활용하는 것이 권장됩니다. (출처: Oracle 공식 블로그, 2026.03.17)
Q2. G1 GC 성능 개선을 누리려면 설정을 바꿔야 하나요?
아무것도 바꾸지 않아도 됩니다. G1은 Java 9부터 기본 GC입니다. JDK 버전을 26으로 올리기만 하면 JEP 522의 듀얼 카드 테이블 구조가 자동 적용됩니다. 객체 참조를 자주 수정하는 워크로드에서 5~15% 처리량 향상을 기대할 수 있습니다. (출처: openjdk.org JEP 522)
Q3. JEP 500 경고가 내 코드에서 나오면 어떻게 해야 하나요?
내 코드라면 final 필드를 수정하는 방식 대신 생성자 주입으로 전환하는 것이 근본 해결책입니다. 서드파티 라이브러리에서 경고가 나온다면 해당 라이브러리의 Java 26 대응 버전이 있는지 확인하세요. Spring Framework 7.0은 이미 정리됐고, Hibernate 7.x와 테스트 프레임워크는 업데이트 여부를 확인해야 합니다. 임시로는 --enable-final-field-mutation=ALL-UNNAMED 플래그로 억제할 수 있습니다.
Q4. HTTP/3 지원이 WebFlux에도 적용되나요?
JEP 517의 HTTP/3 지원은 JDK 표준 HttpClient API에 적용됩니다. Spring WebFlux가 기본으로 사용하는 Reactor Netty는 별도 HTTP/3 지원 로드맵을 갖고 있어서, Java 26 업데이트만으로 WebFlux에 자동 적용되지는 않습니다. RestClient를 JDK HttpClient 백엔드로 구성한 경우에 효과가 있습니다.
Q5. Project Valhalla는 언제 나오나요?
오라클이 공식적으로 출시 일정을 발표하지 않았습니다. 다만 JEP 500(final 불변성 보장)과 JEP 529(Vector API 11차 인큐베이터)가 이번 릴리스에 함께 포함됐다는 점, 그리고 Vector API가 “Value Classes 프리뷰 때까지 인큐베이션 유지”라고 명시하고 있다는 점에서, Java 27 또는 28에서 Valhalla 관련 프리뷰가 처음 등장할 가능성을 커뮤니티가 주목하고 있습니다.

마치며

Java 26은 화려하지 않습니다. JEP가 10개밖에 없고, 새로운 언어 문법도 없습니다. 그런데 막상 들여다보면 실무에 바로 영향을 주는 변화가 꽤 있습니다. G1 GC는 코드 한 줄 건드리지 않아도 빨라졌고, HTTP/3는 외부 라이브러리 없이 JDK 안에 들어왔습니다. 그리고 final 경고는 지금 당장 CI에서 돌려볼 이유가 됩니다.

프로덕션에 올릴 버전은 Java 25 LTS가 맞습니다. 하지만 Java 26을 CI에 넣어두는 건 지금 당장 해야 할 일입니다. JEP 500 경고가 내 의존성 중 어디에서 나오는지, 미리 파악해두지 않으면 나중에 더 큰 비용이 됩니다. 직접 뜯어보니 이번 릴리스는 “심심한 릴리스”가 아니라 “조용하지만 할 일이 있는 릴리스”였습니다.

본 포스팅 참고 자료

  1. Oracle 공식 Java 26 발표 — oracle.com/kr/news/announcement/oracle-releases-java-26-2026-03-17/
  2. Oracle Java 26 기술 블로그 “The Arrival of Java 26” — blogs.oracle.com/java/the-arrival-of-java-26
  3. Oracle JDK 26 Release Notes — oracle.com/java/technologies/javase/26-relnote-issues.html
  4. Spring Boot 개발자를 위한 Java 26 정리 (dev.to) — dev.to/paszekdev/java-26-is-out-…
  5. Java 26 기술 분석 (wikidocs.net) — wikidocs.net/blog/@jaehong/9440/

본 포스팅은 2026년 3월 23일 기준 공식 문서를 토대로 작성됐습니다. 본 포스팅 작성 이후 서비스 정책·UI·기능이 변경될 수 있습니다. JDK 버전 정보: Java 26 (build 26+35, 2026.03.17 GA). 수치 및 기능 사항은 반드시 공식 Oracle 문서에서 최신 내용을 재확인하세요.

댓글 남기기


최신 글


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

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

계속 읽기