JDK 26 / non-LTS
Java 26 업그레이드, 기다렸다가 더 손해인 이유
“non-LTS라서 어차피 쓸 일 없다”고 넘기기엔, 지금 이 버전이 조용히 해놓은 일이 너무 많습니다. G1 GC 처리량이 최대 15% 올라갔고, 오랫동안 방치됐던 Thread.stop()이 완전히 사라졌습니다. 아무것도 안 해도 당신 코드에 영향을 줄 수 있습니다.
아무것도 안 해도 성능이 달라지는 이유
Java 26의 핵심은 JEP 522: G1 GC 처리량 개선입니다. 기존엔 애플리케이션 스레드와 GC 스레드가 단일 카드 테이블을 공유했습니다. 객체 참조를 쓸 때마다 두 쪽이 동기화를 맞춰야 했는데, 이 병목이 레퍼런스가 많은 워크로드에서 눈에 띄게 걸렸습니다. Java 26은 이걸 각자 독립된 듀얼 카드 테이블로 분리했습니다. 결과는 레퍼런스 헤비 워크로드 기준 5~15% 처리량 향상입니다. (출처: dev.to, 2026.03.17)
💡 공식 JEP 문서와 Spring Boot 실사용 분석을 같이 놓고 보니 이런 패턴이 보였습니다 — G1 GC는 Spring Boot 기본값입니다. JPA 엔티티 로드, HTTP 요청 처리 모두 레퍼런스 헤비 워크로드에 해당합니다. 즉, Spring Boot 앱이라면 코드 한 줄 안 바꿔도 이 성능 이득을 자동으로 가져갑니다.
그런데 여기서 좀 더 따져봐야 할 숫자가 있습니다. Java 25에서 이미 도입된 Compact Object Headers는 객체 헤더를 기존 12~16바이트에서 8바이트로 줄여 힙 사용량을 약 22% 감소시킵니다. (출처: dev.to, 2026.03.17) Java 26의 G1 GC 개선까지 더하면, Java 21에서 Java 26으로 올리는 순간 성능 논거가 꽤 단단해집니다.
추가로 JEP 516: 모든 GC를 위한 AOT 객체 캐싱이 포함됐습니다. 기존 AOT 캐시는 ZGC와 함께 쓸 수 없었는데, Java 26에서는 GC에 무관한 중립 포맷으로 객체를 순차 로드해 ZGC 포함 모든 GC에서 시작 시간을 단축합니다. (출처: Oracle 공식 발표, 2026.03.17)
지금 당장 로그를 확인해야 하는 변경
JEP 500: final이 진짜 final이 되는 시작
Java에서 오랫동안 final 필드는 이름만 final이었습니다. 딥 리플렉션을 쓰면 누구든 값을 바꿀 수 있었고, Hibernate, Mockito, Lombok 같은 라이브러리들이 이 방법에 조용히 의존해 왔습니다. Java 26부터는 이 방식을 사용할 때 경고가 찍힙니다.
⚠️ 지금은 경고지만, 다음 버전에서 예외로 격상됩니다. Spring Framework 7.0은 이미 정리됐습니다. Hibernate 6.x 이하, 구버전 Mockito 사용 중이라면 지금 CI 로그를 점검하는 게 훨씬 낫습니다. (출처: dev.to, 2026.03.17)
실제로 어떤 출력이 나오는지 확인하는 방법은 간단합니다.
# JDK 26을 CI에 추가하고 이 경고를 그레픽
# Warning 패턴: "has been mutated reflectively"
--illegal-final-field-mutation=deny # 지금 에러로 잡고 싶을 때
--enable-final-field-mutation=ALL-UNNAMED # 임시 우회용
프로덕션에 올리지 않더라도, CI 매트릭스에 Java 26을 하나 추가해두면 지금 의존하는 라이브러리 중 어느 게 문제인지 먼저 파악할 수 있습니다. 이 준비를 미루면 Java 27이나 28에서 런타임 예외로 터집니다.
HTTP/3가 드디어 표준 라이브러리에 들어왔습니다
JEP 517: HTTP 클라이언트 API에 HTTP/3 지원이 Java 26에 포함됐습니다. 지금까지는 HTTP/3를 쓰려면 Netty나 별도 QUIC 구현체를 직접 끌어와야 했습니다. 이제 JDK에 내장됐습니다. 코드 변경은 단 한 줄입니다.
// Java 26 기준
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_3)
.build();
// 서버가 HTTP/3를 미지원하면 HTTP/2로 자동 폴백
💡 공식 JEP 517 문서와 Spring Boot 실사용 흐름을 같이 놓고 보니 이런 차이가 보였습니다 — WebFlux를 Reactor Netty로 쓰고 있다면 이 변경의 혜택을 직접 받지 못합니다. RestClient가 JDK HttpClient를 백엔드로 쓸 때만 적용됩니다.
HTTP/3가 체감되는 시나리오는 구체적입니다. AI 추론 API 호출, 결제 게이트웨이 연동, 고동시성 외부 API 콜처럼 하나의 느린 요청이 커넥션 전체를 블록하는 상황에서 HTTP/3의 QUIC 기반 멀티플렉싱이 효과를 냅니다. 이런 패턴이 서비스에 있다면 지금 확인해볼 만합니다.
기다려도 안 나오는 기능들, 현실 점검
Java 26 릴리스 노트를 읽다 보면 “또 미리보기”라는 말이 눈에 밟힙니다. 솔직히 말하면, 기대하던 기능 중 상당수가 이번에도 확정되지 않았습니다.
| 기능 | 상태 | 회차 |
|---|---|---|
| Structured Concurrency | 6차 미리보기 | Java 19~26 |
| Vector API | 11차 인큐베이터 | Java 16~26 |
| String Templates | 철회됨 | 재설계 중 |
| Primitive in Patterns | 4차 미리보기 | Java 23~26 |
Vector API는 Project Valhalla의 Value Classes를 기다리는 중이라 Java 26에서도 변경 사항이 없습니다. (출처: dev.to, 2026.03.17) String Templates는 Java 21, 22에서 미리보기까지 나왔다가 설계팀이 “API가 맞지 않다”고 판단해 전면 철회됐습니다. 재도입 시점은 공식적으로 밝혀진 바 없습니다.
Java 25 LTS와 비교하면 지금 올릴 이유가 없습니다
여기서부터가 진짜 핵심입니다. 오라클 공식 발표에는 잘 눈에 안 띄는 문장이 있습니다. “Oracle will provide updates until September 2026 when it will be superseded by Oracle JDK 27.” (출처: Oracle Java 기술 블로그, 2026.03.17)
⚠️ Java 26의 Premier Support는 2026년 9월에 끝납니다. 지금 프로덕션에 올리면 6개월 후 보안 패치 없이 방치되거나, 다시 Java 27으로 올려야 합니다. 장기 지원이 필요한 운영 환경엔 Java 25 LTS가 정답입니다.
그런데 이게 “Java 26을 무시해도 된다”는 뜻은 아닙니다. Spring Boot 4.0.x는 Java 25까지 공식 지원하고, Java 26은 “best effort” 수준입니다. (출처: dev.to, 2026.03.17) 이 말은 지금 Java 26을 프로덕션에 올리면 Spring Boot 쪽 공식 보증이 없다는 뜻입니다. 전략은 이렇습니다. 프로덕션은 Java 25 LTS 유지, CI 파이프라인에 Java 26 추가해 JEP 500 경고 조기 탐지.
Java 21에서 Java 25 LTS로 직접 건너뛸 계획이라면, Compact Object Headers(힙 22% 절감), 가상 스레드 고정 문제 해결, 개선된 AOT 캐싱까지 한꺼번에 챙길 수 있습니다. Java 26의 G1 GC 개선은 그 위에 얹히는 보너스입니다.
Thread.stop() 제거가 예상보다 조용히 터집니다
1998년부터 내려온 메서드가 드디어 사라졌습니다
Thread.stop()은 JDK 1.2(1998)에 사용 중단이 권고됐고, JDK 18에서 제거 예고됐으며, JDK 20부터는 호출하면 무조건 UnsupportedOperationException을 던졌습니다. Java 26에서 메서드 자체가 삭제됐습니다. (출처: JDK 26 Release Notes, 2026.03.17)
💡 JDK 26 Release Notes와 실제 마이그레이션 사례를 교차해 보니 이런 부분이 보였습니다 — 직접 작성한 코드에는 없더라도 레거시 써드파티 JAR 안에 남아 있는 경우가 훨씬 많습니다. Java 26으로 컴파일하면 컴파일 에러, 이전 버전 바이트코드가 Java 26 JVM에서 실행되면 NoSuchMethodError가 발생합니다. 컴파일 에러가 안 난다고 안전한 게 아닙니다.
확인 방법은 간단합니다. 의존성 JAR를 대상으로 jdeps --jdk-internals를 돌리거나, Java 26 JVM에서 전체 테스트 스위트를 돌려 NoSuchMethodError 발생 여부를 확인합니다. CI에 Java 26을 추가해야 하는 또 다른 이유입니다.
UUIDv7, DB 인덱스 단편화를 끊는 방법
JEP로 분류되지 않은 변경이지만, 실용도 면에서는 놓치면 아쉬운 항목입니다. Java 26은 UUID.ofEpochMillis(long) 메서드를 추가했습니다. 이걸로 UUIDv7 즉, 시간 기반 UUID를 별도 라이브러리 없이 만들 수 있습니다. (출처: JDK 26 Release Notes, 2026.03.17)
💡 UUIDv4(무작위)를 기본 키로 쓰면 새 행이 B-트리 인덱스의 임의 위치에 삽입됩니다. 페이지 분할이 반복되면 인덱스 단편화가 쌓입니다. UUIDv7은 앞 비트가 밀리초 타임스탬프여서 항상 인덱스 끝에 삽입됩니다. 라이브러리 의존성을 끊고, 메서드 하나 교체로 DB 성능을 챙길 수 있습니다.
// 기존 (랜덤, 인덱스 단편화 유발)
UUID id = UUID.randomUUID();
// Java 26 이후 (시간 정렬, 인덱스 효율적)
UUID id = UUID.ofEpochMillis(System.currentTimeMillis());
추가로 Process 클래스가 AutoCloseable을 구현해 try-with-resources로 프로세스 자원을 자동 해제할 수 있게 됐고, JavaDoc에 다크 모드가 추가됐습니다. JavaDoc 문서를 자주 보는 팀이라면 체감이 바로 됩니다.
Q&A
마치며
Java 26은 조용한 버전입니다. 새 언어 문법이 확정된 게 없고, 오래 기다리던 기능들은 또 미리보기로 남았습니다. 그런데 조용한 것과 무관심해도 되는 건 다릅니다.
G1 GC 처리량이 5~15% 올라갔고, HTTP/3가 JDK 표준에 들어왔습니다. AOT 캐싱이 ZGC에서도 돌아갑니다. UUIDv7이 메서드 하나로 해결됩니다. 그리고 JEP 500의 경고는 지금 내 코드가 아니라 내가 쓰는 라이브러리 안에서 이미 울리고 있을 수 있습니다.
프로덕션은 Java 25 LTS로 유지하되, CI에 Java 26을 하나 추가해 두는 게 지금 시점에서 가장 실리 있는 선택입니다. 6개월 후 Java 27이 나오면 그때 다시 평가하면 됩니다. 기다리는 동안 JEP 500 경고가 쌓이는 건 지금 잡는 게 낫습니다.
막상 해보면 생각보다 간단합니다. CI 설정 파일에 Java 26 한 줄 추가하고, 로그에 “mutated reflectively”가 뜨는지 확인하는 것부터 시작입니다.
본 포스팅 참고 자료
- Oracle 공식 발표 — Oracle Releases Java 26 (2026.03.17): https://www.oracle.com/kr/news/announcement/oracle-releases-java-26-2026-03-17/
- JDK 26 공식 Release Notes (2026.03.17): https://jdk.java.net/26/release-notes
- Oracle Java 기술 블로그 — The Arrival of Java 26: https://blogs.oracle.com/java/the-arrival-of-java-26
- dev.to — Java 26 Is Out: What Actually Matters for Spring Boot Developers (2026.03.17): https://dev.to/paszekdev/java-26-is-out…
본 포스팅은 2026년 3월 21일 기준으로 작성됐습니다. Java 26(JDK 26) 및 관련 공식 문서 기준이며, 본 포스팅 작성 이후 서비스 정책·UI·기능이 변경될 수 있습니다. 최신 정보는 Oracle 공식 사이트 및 OpenJDK 공식 문서를 직접 확인하시기 바랍니다.

댓글 남기기