JDK 21์—์„œ JDK 25๋กœ: ์„ฑ๋Šฅ ์—…๋ฐ์ดํŠธ

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” JDK 21์—์„œ JDK 25๊นŒ์ง€์˜ JDK 25 ์„ฑ๋Šฅ ๊ฐœ์„  ์‚ฌํ•ญ์— ๋Œ€ํ•ด์„œ ์ •๋ฆฌํ•˜๊ณ ์ž ํ•œ๋‹ค. 2026๋…„ 3์›” Jfokus ์ปจํผ๋Ÿฐ์Šค์—์„œ Oracle์˜ Claes Redestad์™€ Per-Ake Minborg๊ฐ€ Inside Java์— ๋ฐœํ‘œํ•œ ๋‚ด์šฉ์„ ์ค‘์‹ฌ์œผ๋กœ, ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ถ€ํ„ฐ JIT ์ปดํŒŒ์ผ๋Ÿฌ, ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ๊นŒ์ง€ 13๊ฐ€์ง€ ๊ฐœ์„  ์‚ฌํ•ญ์„ ์‚ดํŽด๋ณธ๋‹ค. Timefold๊ฐ€ ์‹ค์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ ์ธก์ •ํ•œ ๊ฒฐ๊ณผ JDK 25๋Š” JDK 21 ๋Œ€๋น„ ์•ฝ 8% ๋น ๋ฅธ ๊ฒƒ์œผ๋กœ ๋‚˜ํƒ€๋‚ฌ๋Š”๋ฐ, JAR ํŒŒ์ผ์€ ๊ทธ๋Œ€๋กœ ๋‘๊ณ  JVM ๋ฒ„์ „๋งŒ ์˜ฌ๋ ธ์„ ๋•Œ์˜ ์ˆ˜์น˜๋‹ค. JDK 25๋Š” JDK 21์˜ ๋’ค๋ฅผ ์ž‡๋Š” ๋‹ค์Œ LTS ๋ฆด๋ฆฌ์ฆˆ๋‹ค. Spring Boot 3.x ํ™˜๊ฒฝ์ด๋ผ๋ฉด ์—…๊ทธ๋ ˆ์ด๋“œ๋งŒ์œผ๋กœ JDK 25 ์„ฑ๋Šฅ ๊ฐœ์„ ์˜ ํšจ๊ณผ๋ฅผ ์ฆ‰์‹œ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.


๋ชฉ์ฐจ

JDK 25 ์„ฑ๋Šฅ ๊ฐœ์„ ์˜ ์ „์ฒด ๊ทธ๋ฆผ: 13๊ฐ€์ง€ ๊ฐœ์„ , 3๊ฐœ ๋ ˆ์ด์–ด

JDK 25๋Š” 3,200๊ฐœ ์ด์ƒ์˜ ์ด์Šˆ๋ฅผ ์ˆ˜์ •ํ–ˆ์œผ๋ฉฐ, ์ด ์ค‘ ์•ฝ 1,000๊ฐœ๊ฐ€ ๊ฐœ์„ ์‚ฌํ•ญ์ด๊ณ  ์•ฝ 100๊ฐœ๊ฐ€ ์„ฑ๋Šฅ ๊ด€๋ จ์œผ๋กœ ๋ช…์‹œ๋˜์—ˆ๋‹ค. ๋ฐœํ‘œ์—์„œ ๊ฐ•์กฐ๋œ 13๊ฐ€์ง€ ๊ตฌ์ฒด์ ์ธ JDK 25 ์„ฑ๋Šฅ ๊ฐœ์„  ์‚ฌํ•ญ์€ ํฌ๊ฒŒ ์„ธ ๋ ˆ์ด์–ด๋กœ ๊ตฌ๋ถ„๋œ๋‹ค.

๋ ˆ์ด์–ด์ฃผ์š” ๊ฐœ์„  ์‚ฌํ•ญ๋Œ€ํ‘œ ์ˆ˜์น˜
ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌString hashCode, Memory Segment, BigDecimal, Vector API์ตœ๋Œ€ 14๋ฐฐ ํ–ฅ์ƒ
JIT ์ปดํŒŒ์ผ๋ŸฌC2 ์ž๋™ ๋ฒกํ„ฐํ™”, ์ƒ์ˆ˜ ํด๋”ฉ, ๋งˆ์Šคํฌ ์ฒดํฌ์ตœ๋Œ€ 10,000๋ฐฐ ํ–ฅ์ƒ
๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐCompact Object Headers, G1 Remembered Set, Generational ZGCํž™ 20% ์ ˆ๊ฐ

๊ฐ ๋ ˆ์ด์–ด์˜ ๊ฐœ์„ ์€ ๋…๋ฆฝ์ ์œผ๋กœ ์ž‘๋™ํ•˜๋ฏ€๋กœ, ํŠน์ • ์›Œํฌ๋กœ๋“œ์—์„œ๋Š” ์„ธ ๋ ˆ์ด์–ด์˜ ํšจ๊ณผ๊ฐ€ ์ค‘์ฒฉ๋˜์–ด ํ›จ์”ฌ ํฐ ํญ์˜ JDK 25 ์„ฑ๋Šฅ ๊ฐœ์„ ์„ ์ฒด๊ฐํ•  ์ˆ˜ ์žˆ๋‹ค.


๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ ˆ๋ฒจ: ์ฝ”๋“œ ํ•œ ์ค„ ์•ˆ ๋ฐ”๊พธ๊ณ  ์–ป๋Š” ์„ฑ๋Šฅ ํ–ฅ์ƒ

JDK 25๋Š” ์ž์ฃผ ์“ฐ๋Š” ๋‚ด์žฅ ํด๋ž˜์Šค๋“ค์„ ๋‚ด๋ถ€์ ์œผ๋กœ ์ตœ์ ํ™”ํ–ˆ๋‹ค. ์ฝ”๋“œ๋Š” ๊ทธ๋Œ€๋กœ ๋‘๊ณ  JVM๋งŒ ์˜ฌ๋ ค๋„ ๋‹ค์Œ ํ•ญ๋ชฉ์—์„œ ์„ฑ๋Šฅ ํ–ฅ์ƒ์ด ์ฒด๊ฐ๋œ๋‹ค.

  • String hashCode (8๋ฐฐ ํ–ฅ์ƒ): HashMap/HashSet์—์„œ ์“ฐ์ด๋Š” ํ•ด์‹œ ๊ณ„์‚ฐ์ด ๋นจ๋ผ์ง„๋‹ค. ๋ฌธ์ž์—ด์„ ํ‚ค๋กœ ์ž์ฃผ ์“ฐ๋Š” ์„œ๋น„์Šค ๋ ˆ์ด์–ด๋‚˜ ์บ์‹œ ์ฝ”๋“œ์—์„œ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ˜œํƒ์„ ๋ฐ›๋Š”๋‹ค.
  • BigDecimal (6~9๋ฐฐ ํ–ฅ์ƒ): BigDecimal.valueOf() ํ˜ธ์ถœ์ด ๋นจ๋ผ์ง„๋‹ค. ๊ธˆ์•ก ๊ณ„์‚ฐ์ด๋‚˜ ํ†ต๊ณ„ ์ฒ˜๋ฆฌ ์ฝ”๋“œ์— ์ง์ ‘ ํšจ๊ณผ๊ฐ€ ์žˆ๋‹ค.
  • Math ํ•จ์ˆ˜ (3~5๋ฐฐ ํ–ฅ์ƒ): Math.max(), Math.min(), Math.cbrt() ๊ฐ™์€ ๊ธฐ๋ณธ ์ˆ˜ํ•™ ํ•จ์ˆ˜๊ฐ€ ๋นจ๋ผ์ง„๋‹ค.
  • ๋„ค์ดํ‹ฐ๋ธŒ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น (์ตœ๋Œ€ 2๋ฐฐ ํ–ฅ์ƒ): FFM API๋กœ ๋„ค์ดํ‹ฐ๋ธŒ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ง์ ‘ ๋‹ค๋ฃจ๋Š” ์ฝ”๋“œ์—์„œ ํ• ๋‹น ์†๋„๊ฐ€ ๋นจ๋ผ์ง„๋‹ค.

JIT ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋” ์Šค๋งˆํŠธํ•ด์กŒ๋‹ค

JIT ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ์‹คํ–‰ ์ค‘์ธ Java ์ฝ”๋“œ๋ฅผ ๊ธฐ๊ณ„์–ด๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค. JDK 25์—์„œ๋Š” ์ด ๊ณผ์ •์—์„œ ๋ถˆํ•„์š”ํ•œ ์—ฐ์‚ฐ์„ ๋” ์ž˜ ์ œ๊ฑฐํ•œ๋‹ค.

  • ๋ฐฐ์—ด ๋ฃจํ”„ ์ฒ˜๋ฆฌ (33๋ฐฐ ํ–ฅ์ƒ): ์ˆซ์ž ๋ฐฐ์—ด์„ ํ•ฉ์‚ฐํ•˜๊ฑฐ๋‚˜ ๋ณ€ํ™˜ํ•˜๋Š” ๋ฃจํ”„๊ฐ€ ๋‚ด๋ถ€์ ์œผ๋กœ ์—ฌ๋Ÿฌ ์š”์†Œ๋ฅผ ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌํ•˜๋„๋ก ์ž๋™ ๋ณ€ํ™˜๋œ๋‹ค. ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ๋‚˜ ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ ๊ณ„์‚ฐ ์ฝ”๋“œ์— ํšจ๊ณผ๊ฐ€ ๋‚˜ํƒ€๋‚œ๋‹ค.
  • ์กฐ๊ฑด ๋ถ„๊ธฐ ์ œ๊ฑฐ (์ตœ๋Œ€ 10,000๋ฐฐ ํ–ฅ์ƒ): static final ์ƒ์ˆ˜๋ฅผ ์ด์šฉํ•œ ์กฐ๊ฑด ๊ฒ€์‚ฌ์—์„œ ๊ฒฐ๊ณผ๊ฐ€ ํ•ญ์ƒ ๋™์ผํ•˜๋‹ค๊ณ  ํŒ๋‹จ๋˜๋ฉด ํ•ด๋‹น ๋ฃจํ”„๋ฅผ ์ฝ”๋“œ์—์„œ ์•„์˜ˆ ์ œ๊ฑฐํ•œ๋‹ค. ์ˆ˜์น˜๊ฐ€ ๊ทน๋‹จ์ ์œผ๋กœ ํฐ ๊ฒƒ์€ ๋ฃจํ”„ ์ž์ฒด๊ฐ€ ์—†์–ด์ง€๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
  • Math ์—ฐ์‚ฐ (3~5๋ฐฐ ํ–ฅ์ƒ): Math.max(), Math.min() ๊ฐ™์€ ๊ธฐ๋ณธ ์—ฐ์‚ฐ์ด ๋‹จ์ผ CPU ๋ช…๋ น์–ด๋กœ ๋ณ€ํ™˜๋œ๋‹ค.

GC ํ˜์‹ : ๋ฉ”๋ชจ๋ฆฌ๋„ ๋ฉˆ์ถค๋„, ๋ชจ๋‘ ์žก์•˜๋‹ค

GC(๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ)๋Š” ์‚ฌ์šฉ์ด ๋๋‚œ ๊ฐ์ฒด๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์ •๋ฆฌํ•˜๋Š” JVM ์ปดํฌ๋„ŒํŠธ๋‹ค. JDK 25์—์„œ๋Š” ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰๊ณผ GC ๋นˆ๋„ ๋ชจ๋‘ ๊ฐœ์„ ๋๋‹ค.

  • ๊ฐ์ฒด ํ—ค๋” ์••์ถ• (JEP 519): ํž™์— ์žˆ๋Š” ๋ชจ๋“  ๊ฐ์ฒด์˜ ํ—ค๋”๊ฐ€ 4๋ฐ”์ดํŠธ ์ค„์–ด๋“ ๋‹ค. ํž™ ์ „์ฒด ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์ตœ๋Œ€ 20% ๊ฐ์†Œํ•˜๊ณ  GC ๋ฐœ์ƒ ๋นˆ๋„๋„ 15% ๋‚ฎ์•„์ง„๋‹ค. -XX:+UseCompactObjectHeaders ํ”Œ๋ž˜๊ทธ ํ•˜๋‚˜๋กœ ์ฆ‰์‹œ ์ ์šฉ๋œ๋‹ค.

Java25 Compact Object Header๋กœ HotSpot JVM์—์„œ ๋ฉ”๋ชจ๋ฆฌ ์ ˆ์•ฝ ๊ธ€ ์ฐธ์กฐ

  • G1 GC ๋‚ด๋ถ€ ์ตœ์ ํ™”: G1์ด ๋‚ด๋ถ€์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ์ฐธ์กฐ ์ถ”์  ๋ฐ์ดํ„ฐ ํฌ๊ธฐ๊ฐ€ 60% ์ค„์—ˆ๋‹ค. 64GB ํž™ ๊ธฐ์ค€ 2GB โ†’ 0.75GB ์ˆ˜์ค€์ด๋‹ค. ๋ณ„๋„ ์„ค์ • ์—†์ด JDK 25๋กœ ์˜ฌ๋ฆฌ๋ฉด ์ž๋™ ์ ์šฉ๋œ๋‹ค.
  • ZGC (GC CPU 25~40% ์ ˆ๊ฐ): java -XX:+UseZGC ํ•˜๋‚˜๋ฉด Generational ZGC๊ฐ€ ๊ธฐ๋ณธ ์ ์šฉ๋œ๋‹ค. GC์— ์†Œ๋น„๋˜๋Š” CPU๊ฐ€ ํฌ๊ฒŒ ์ค„์–ด ๋ ˆ์ดํ„ด์‹œ ์šฐ์„  ํ™˜๊ฒฝ์—์„œ ์œ ์šฉํ•˜๋‹ค.

์„ธ ํ•ญ๋ชฉ ๋ชจ๋‘ Spring Boot ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ฝ”๋“œ ์ˆ˜์ • ์—†์ด ์ ์šฉ๋œ๋‹ค. ๊ทธ ์ค‘ -XX:+UseCompactObjectHeaders๋Š” ํ”Œ๋ž˜๊ทธ ํ•œ ์ค„์ด๋ผ๋Š” ๋‚ฎ์€ ๋น„์šฉ์œผ๋กœ ๊ฐ€์žฅ ์ฆ‰๊ฐ์ ์ธ ํšจ๊ณผ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.


Stable Values ์™„์ „ ์ •๋ณต: ์ง€์—ฐ ์ดˆ๊ธฐํ™”์™€ JIT ์ตœ์ ํ™”๋ฅผ ๋™์‹œ์—

์™œ Stable Values๊ฐ€ ํ•„์š”ํ•œ๊ฐ€?

JDK 25 ์„ฑ๋Šฅ ๊ฐœ์„  ์ค‘ ๊ฐ€์žฅ ์ฃผ๋ชฉ๋ฐ›๋Š” ์‹ ๊ธฐ๋Šฅ์ธ Stable Values(JEP 502)๋Š” ๊ธฐ์กด Java์— ์กด์žฌํ•˜๋˜ ๋”œ๋ ˆ๋งˆ๋ฅผ ํ•ด๊ฒฐํ•œ๋‹ค. ์ง€๊ธˆ๊นŒ์ง€ ๊ฐœ๋ฐœ์ž๋Š” ๋‘ ๊ฐ€์ง€ ์„ ํƒ์ง€๋ฐ–์— ์—†์—ˆ๋‹ค.

์ฒซ ๋ฒˆ์งธ๋Š” final ํ•„๋“œ๋‹ค. JIT ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ƒ์ˆ˜๋กœ ์ธ์‹ํ•˜์—ฌ ์ตœ์ ํ™”(constant folding)๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๋ฐ˜๋“œ์‹œ ์ƒ์„ฑ์ž๋‚˜ ์ •์  ์ดˆ๊ธฐํ™” ๋ธ”๋ก์—์„œ ์ฆ‰์‹œ ์ดˆ๊ธฐํ™”ํ•ด์•ผ ํ•œ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘ ์‹œ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ•œ๊บผ๋ฒˆ์— ์ดˆ๊ธฐํ™”ํ•˜๋ฉด ์‹œ์ž‘ ์‹œ๊ฐ„์ด ๊ธธ์–ด์ง„๋‹ค.

๋‘ ๋ฒˆ์งธ๋Š” ์ด์ค‘ ํ™•์ธ ์ž ๊ธˆ(Double-Checked Locking) ํŒจํ„ด์ด๋‹ค. ์ดˆ๊ธฐํ™”๋ฅผ ์ง€์—ฐ์‹œํ‚ฌ ์ˆ˜ ์žˆ์ง€๋งŒ volatile ์„ ์–ธ์ด ํ•„์š”ํ•˜๊ณ , JIT ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ƒ์ˆ˜๋กœ ์ธ์‹ํ•˜์ง€ ๋ชปํ•ด ์ตœ์ ํ™”๊ฐ€ ์ ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค.

StableValue<T>๋Š” ์ด ๋‘˜์„ ๋™์‹œ์— ํ•ด๊ฒฐํ•œ๋‹ค. ์ดˆ๊ธฐํ™” ์‹œ์ ์€ ์ž์œ ๋กญ๊ฒŒ ๋ฏธ๋ฃฐ ์ˆ˜ ์žˆ๊ณ , ์ดˆ๊ธฐํ™”๊ฐ€ ๋๋‚˜๋ฉด JIT์ด ๊ทธ ๊ฐ’์„ ์ƒ์ˆ˜๋กœ ์ธ์‹ํ•ด์„œ ์ตœ์ ํ™”๋ฅผ ์ ์šฉํ•œ๋‹ค.

StableValue ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

import java.lang.StableValue; // java.lang ํŒจํ‚ค์ง€ โ€” ์‹ค์ œ๋กœ๋Š” ์ž๋™ ์ž„ํฌํŠธ๋˜์–ด ์ƒ๋žต ๊ฐ€๋Šฅ
import java.util.function.Supplier;

// ์ปดํŒŒ์ผ ๋ฐ ์‹คํ–‰ ์‹œ --enable-preview ํ•„์š” (JDK 25 ํ”„๋ฆฌ๋ทฐ ๊ธฐ๋Šฅ)
public class OrderService {

    // final์ฒ˜๋Ÿผ ์„ ์–ธํ•˜๋˜, ์ดˆ๊ธฐํ™”๋Š” ์ฒ˜์Œ ์‚ฌ์šฉ ์‹œ์ ์œผ๋กœ ๋ฏธ๋ฃธ
    private final StableValue<AuditLogger> auditLogger = StableValue.of();

    // ์ฒ˜์Œ ํ˜ธ์ถœ ์‹œ ์ดˆ๊ธฐํ™”, ์ดํ›„ JIT์ด ์ƒ์ˆ˜๋กœ ์ฒ˜๋ฆฌ
    private AuditLogger getAuditLogger() {
        // ๋žŒ๋‹ค๊ฐ€ ์ฒ˜์Œ ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰๋จ (๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ์•ˆ์ „, ์Šน์ž ๋ฐฉ์‹)
        return auditLogger.orElseSet(() -> AuditLogger.create(OrderService.class));
    }

    public void placeOrder(Order order) {
        // ์ฃผ๋ฌธ ์ฒ˜๋ฆฌ ๋กœ์ง
        getAuditLogger().log("Order placed: " + order.getId());
    }
}
Java

orElseSet()์€ ์ฒซ ํ˜ธ์ถœ ์‹œ๋งŒ ๋žŒ๋‹ค๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ์ดํ›„์—๋Š” ์บ์‹œ๋œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ๋„ ์ดˆ๊ธฐํ™”๋Š” ์ตœ๋Œ€ ํ•œ ๋ฒˆ๋งŒ ๋ณด์žฅ๋˜๊ณ , ์ดˆ๊ธฐํ™”๊ฐ€ ๋๋‚˜๋ฉด JIT์ด ํ•ด๋‹น ๊ฐ’์„ ๋ถˆ๋ณ€ ์ƒ์ˆ˜๋กœ ์ทจ๊ธ‰ํ•œ๋‹ค.

Stable Supplier: ๋” ์ฝ๊ธฐ ์ข‹์€ ํŒจํ„ด

public class ProductRepository {

    // Supplier ํ˜•ํƒœ๋กœ ์„ ์–ธํ•˜๋ฉด ์„ ์–ธ๋ถ€์™€ ์ดˆ๊ธฐํ™” ๋กœ์ง์ด ์ธ์ ‘ํ•˜์—ฌ ๊ฐ€๋…์„ฑ ํ–ฅ์ƒ
    private final Supplier<DatabaseConnection> dbConnection =
        StableValue.supplier(() -> DatabasePool.acquire("product-db"));

    public Product findById(Long id) {
        // dbConnection.get() ํ˜ธ์ถœ ์‹œ ์ฒ˜์Œ์—๋งŒ ํ’€์—์„œ ์ปค๋„ฅ์…˜ ํš๋“
        return dbConnection.get().query(
            "SELECT * FROM products WHERE id = ?", id
        );
    }
}
Java

StableValue.supplier()๋Š” Supplier<T>๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ ๊ธฐ์กด ์ฝ”๋“œ์™€ ํ˜ธํ™˜์„ฑ์ด ์ข‹๋‹ค. ์„ ์–ธ๊ณผ ์ดˆ๊ธฐํ™” ๋กœ์ง์ด ํ•œ ๊ณณ์— ๋ชจ์—ฌ ์žˆ์–ด ๋‚˜์ค‘์— ์ฝ”๋“œ๋ฅผ ์ฝ๊ธฐ๊ฐ€ ์ˆ˜์›”ํ•˜๋‹ค.

Stable List: ์Šค๋ ˆ๋“œ ๋กœ์ปฌ ํ’€ ํŒจํ„ด

public class ConnectionPoolManager {

    private static final int POOL_SIZE = Runtime.getRuntime().availableProcessors();

    // ๊ณ ์ • ํฌ๊ธฐ ๋ชฉ๋ก์œผ๋กœ ์ง€์—ฐ ์ดˆ๊ธฐํ™” โ€” ๊ฐ ์š”์†Œ๋Š” ๋…๋ฆฝ์ ์œผ๋กœ ์ดˆ๊ธฐํ™”๋จ
    private static final List<RedisConnection> REDIS_POOL =
        StableValue.list(POOL_SIZE, index -> RedisConnection.create("localhost", 6379));

    public static RedisConnection getConnection() {
        // ์Šค๋ ˆ๋“œ ID ๊ธฐ๋ฐ˜์œผ๋กœ ์ปค๋„ฅ์…˜ ์„ ํƒ โ€” ๊ฒฝํ•ฉ ์—†์ด ๊ฐ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ณ ์ • ์ปค๋„ฅ์…˜ ์‚ฌ์šฉ
        int index = (int)(Thread.currentThread().threadId() % POOL_SIZE);
        return REDIS_POOL.get(index); // ์ฒ˜์Œ ์ ‘๊ทผ ์‹œ๋งŒ ์ดˆ๊ธฐํ™”
    }
}
Java

StableValue.list()๋Š” ๊ฐ™์€ ์ดˆ๊ธฐํ™” ํŒจํ„ด์ด ๋ฐ˜๋ณต๋˜๋Š” ๊ฐ์ฒด ํ’€์— ์ ํ•ฉํ•˜๋‹ค. ๊ฐ ์ธ๋ฑ์Šค๋Š” ๋…๋ฆฝ์ ์œผ๋กœ ์ง€์—ฐ ์ดˆ๊ธฐํ™”๋˜๋ฏ€๋กœ ์‹ค์ œ๋กœ ์‚ฌ์šฉ๋˜๋Š” ์ปค๋„ฅ์…˜๋งŒ ์ดˆ๊ธฐํ™” ๋น„์šฉ์„ ์น˜๋ฅธ๋‹ค.

final, StableValue, volatile ๋น„๊ต

ํ•ญ๋ชฉfinal ํ•„๋“œStableValue<T>volatile ํ•„๋“œ
์ดˆ๊ธฐํ™” ์‹œ์ ์ƒ์„ฑ์ž/์ •์  ์ดˆ๊ธฐํ™” ๋ธ”๋ก (์ฆ‰์‹œ)์ž„์˜์˜ ์‹œ์  (์ง€์—ฐ ๊ฐ€๋Šฅ)์ž„์˜์˜ ์‹œ์ 
์ƒ์ˆ˜ ํด๋”ฉ ์ตœ์ ํ™”๊ฐ€๋Šฅ (ํ•ญ์ƒ)๊ฐ€๋Šฅ (์ดˆ๊ธฐํ™” ์ดํ›„)๋ถˆ๊ฐ€๋Šฅ
๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ์•ˆ์ „N/A (๋ณ€๊ฒฝ ๋ถˆ๊ฐ€)์•ˆ์ „ (์Šน์ž ๋ฐฉ์‹)์•ˆ์ „ (volatile ๋ณด์žฅ)
์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘ ์˜ํ–ฅ๋†’์Œ (๋ชจ๋‘ ์ฆ‰์‹œ ์ดˆ๊ธฐํ™”)๋‚ฎ์Œ (ํ•„์š” ์‹œ ์ดˆ๊ธฐํ™”)๋‚ฎ์Œ

Stable Values ํ™œ์„ฑํ™” ๋ฐฉ๋ฒ•

# ์ปดํŒŒ์ผ ์‹œ ํ”„๋ฆฌ๋ทฐ ๊ธฐ๋Šฅ ํ™œ์„ฑํ™” (JDK 25)
javac --release 25 --enable-preview src/main/java/**/*.java

# Maven์—์„œ ํ™œ์„ฑํ™”
# pom.xml์— ์•„๋ž˜ ์„ค์ • ์ถ”๊ฐ€
ShellScript
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.13.0</version>
    <configuration>
        <!-- JDK 25 Stable Values ๋“ฑ ํ”„๋ฆฌ๋ทฐ ๊ธฐ๋Šฅ ํ™œ์„ฑํ™” -->
        <compilerArgs>
            <arg>--enable-preview</arg>
        </compilerArgs>
        <release>25</release>
    </configuration>
</plugin>
XML

Maven ์ปดํŒŒ์ผ๋Ÿฌ ํ”Œ๋Ÿฌ๊ทธ์ธ์— --enable-preview์™€ <release>25</release>๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด Stable Values๋ฅผ ํฌํ•จํ•œ JDK 25 ํ”„๋ฆฌ๋ทฐ ๊ธฐ๋Šฅ์„ ์“ธ ์ˆ˜ ์žˆ๋‹ค. ๋‹ค๋งŒ ํ”„๋ฆฌ๋ทฐ๋Š” JDK 26์—์„œ ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ์œผ๋‹ˆ ์šด์˜ ํ™˜๊ฒฝ ๋„์ž… ์ „์—๋Š” ๋ณ„๋„ ๊ฒ€์ฆ์ด ํ•„์š”ํ•˜๋‹ค.


Project Leyden: AOT ๋ฉ”์„œ๋“œ ํ”„๋กœํŒŒ์ผ๋ง์œผ๋กœ ์‹œ์ž‘ ์‹œ๊ฐ„ 15~25% ๋‹จ์ถ•

JEP 515(Ahead-of-Time Method Profiling)๋Š” Project Leyden์˜ ์ผํ™˜์œผ๋กœ JDK 25์— ๋„์ž…๋œ ๊ธฐ๋Šฅ์ด๋‹ค. ์ด ๊ธฐ๋Šฅ์€ ํ›ˆ๋ จ ์‹คํ–‰(training run) ์ค‘ ์ˆ˜์ง‘๋œ ๋ฉ”์„œ๋“œ ์‹คํ–‰ ํ”„๋กœํŒŒ์ผ์„ ์บ์‹œํ•˜์—ฌ, ๋‹ค์Œ ์‹คํ–‰ ์‹œ JIT ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์›Œ๋ฐ์—…์„ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ์ฆ‰์‹œ ์ตœ์ ํ™”๋œ ๋„ค์ดํ‹ฐ๋ธŒ ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค.

# JEP 515 AOT ๋ฐฉ๋ฒ•๋ก : Project Leyden์˜ CDS ๊ธฐ๋ฐ˜ 3๋‹จ๊ณ„ ์›Œํฌํ”Œ๋กœ

# 1๋‹จ๊ณ„: ํ›ˆ๋ จ ์‹คํ–‰ โ€” ๋ฉ”์„œ๋“œ ์‹คํ–‰ ํ”„๋กœํŒŒ์ผ ๋ฐ CDS ์„ค์ • ๊ธฐ๋ก
java -XX:AOTMode=record \
     -XX:AOTConfiguration=myapp.aotconf \
     -jar myapp.jar

# 2๋‹จ๊ณ„: AOT ์บ์‹œ ์ƒ์„ฑ โ€” ์ˆ˜์ง‘๋œ ํ”„๋กœํŒŒ์ผ๋กœ ์ตœ์ ํ™”๋œ ์บ์‹œ ๋นŒ๋“œ
java -XX:AOTMode=create \
     -XX:AOTConfiguration=myapp.aotconf \
     -XX:AOTCache=myapp.aot

# 3๋‹จ๊ณ„: AOT ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‹คํ–‰ โ€” ์›Œ๋ฐ์—… ์—†์ด ์ฆ‰์‹œ ์ตœ์ ํ™”๋œ ์ฝ”๋“œ ์‹คํ–‰
java -XX:AOTCache=myapp.aot -jar myapp.jar

# Spring Boot ์˜ˆ์‹œ: 3๋‹จ๊ณ„ ์ ์šฉ ํ›„ 15-25% ๋น ๋ฅธ ์‹œ์ž‘
java -XX:AOTCache=springapp.aot \
     -Dspring.profiles.active=prod \
     -jar spring-app.jar
ShellScript

ํ›ˆ๋ จ ์‹คํ–‰(1๋‹จ๊ณ„)์—์„œ ์ˆ˜์ง‘๋œ ํ”„๋กœํŒŒ์ผ์—๋Š” ์–ด๋–ค ๋ฉ”์„œ๋“œ๊ฐ€ ์–ผ๋งˆ๋‚˜ ์ž์ฃผ, ์–ด๋–ค ํƒ€์ž…์œผ๋กœ ํ˜ธ์ถœ๋˜์—ˆ๋Š”์ง€๊ฐ€ ๊ธฐ๋ก๋œ๋‹ค. ์ดํ›„ ์‹คํ–‰์—์„œ JIT์€ ์ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์›Œ๋ฐ์—… ์—†์ด ๋ฐ”๋กœ ์ตœ์ ํ™”๋œ ๋„ค์ดํ‹ฐ๋ธŒ ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ์‹œ์ž‘ ์‹œ๊ฐ„ 15~25% ๋‹จ์ถ•์ด๋ผ๋Š” ์ˆ˜์น˜๋Š” ์‹ค์ œ ์ธก์ •๊ฐ’์ด๊ณ , “Hello World” ์ˆ˜์ค€์˜ ๋‹จ์ˆœ ์•ฑ๋„ 28.7ms์—์„œ 25.5ms๋กœ ์ค„์—ˆ๋‹ค(12% ํ–ฅ์ƒ).


JDK 21 โ†’ JDK 25 ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์‹ค์ „ ๊ฐ€์ด๋“œ

Maven/Gradle ํ”„๋กœ์ ํŠธ ์„ค์ • ์—…๋ฐ์ดํŠธ

<!-- pom.xml: Java 25 ๊ธฐ๋ฐ˜ Spring Boot ํ”„๋กœ์ ํŠธ ์„ค์ • -->
<properties>
    <!-- Java ๋ฒ„์ „์„ 21์—์„œ 25๋กœ ๋ณ€๊ฒฝ -->
    <java.version>25</java.version>
    <spring-boot.version>3.5.0</spring-boot.version>
</properties>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <release>25</release>
                <!-- Stable Values ๋“ฑ ํ”„๋ฆฌ๋ทฐ ๊ธฐ๋Šฅ ์‚ฌ์šฉ ์‹œ๋งŒ ์ถ”๊ฐ€ -->
                <!-- <compilerArgs><arg>--enable-preview</arg></compilerArgs> -->
            </configuration>
        </plugin>
    </plugins>
</build>
XML
// build.gradle: Gradle ํ”„๋กœ์ ํŠธ ์„ค์ •
java {
    // Java 21์—์„œ 25๋กœ ๋ณ€๊ฒฝ
    sourceCompatibility = JavaVersion.VERSION_25
    targetCompatibility = JavaVersion.VERSION_25
}

tasks.withType(JavaCompile).configureEach {
    options.release = 25
    // Stable Values ๋“ฑ ํ”„๋ฆฌ๋ทฐ ๊ธฐ๋Šฅ ์‚ฌ์šฉ ์‹œ๋งŒ ์ถ”๊ฐ€
    // options.compilerArgs += ['--enable-preview']
}

tasks.withType(Test).configureEach {
    // ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์‹œ ํ”„๋ฆฌ๋ทฐ ๊ธฐ๋Šฅ ํ™œ์„ฑํ™” (์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ)
    // jvmArgs(['--enable-preview'])
}
Groovy

Maven๊ณผ Gradle ๋ชจ๋‘ release๋ฅผ 25๋กœ ๋ฐ”๊พธ๋Š” ๊ฒƒ๋งŒ์œผ๋กœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ตœ์ ํ™”, JIT ํ–ฅ์ƒ, GC ๊ฐœ์„ ์„ ๋ชจ๋‘ ์–ป๋Š”๋‹ค. --enable-preview๋Š” Stable Values์ฒ˜๋Ÿผ ํ”„๋ฆฌ๋ทฐ ๊ธฐ๋Šฅ์„ ์ง์ ‘ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์—๋งŒ ํ•„์š”ํ•˜๋‹ค.

๊ถŒ์žฅ JVM ํ”Œ๋ž˜๊ทธ ์กฐํ•ฉ

# ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ ๊ถŒ์žฅ JVM ํ”Œ๋ž˜๊ทธ (JDK 25 ๊ธฐ์ค€)
java \
  -XX:+UseG1GC \                         # G1 GC (JDK 25 ๊ธฐ๋ณธ๊ฐ’, ๋ช…์‹œ์  ์ง€์ •)
  -XX:+UseCompactObjectHeaders \          # ๊ฐ์ฒด ํ—ค๋” 4๋ฐ”์ดํŠธ ์ถ•์†Œ, ํž™ 20% ์ ˆ๊ฐ
  -Xms2g -Xmx4g \                        # ํž™ ํฌ๊ธฐ (ํ™˜๊ฒฝ์— ๋งž๊ฒŒ ์กฐ์ •)
  -XX:MaxGCPauseMillis=200 \             # G1 ๋ชฉํ‘œ ์ค‘๋‹จ ์‹œ๊ฐ„
  -XX:+ParallelRefProcEnabled \          # ๋ณ‘๋ ฌ ์ฐธ์กฐ ์ฒ˜๋ฆฌ
  -Xlog:gc*:file=gc.log:time,uptime \   # GC ๋กœ๊ทธ
  -jar myapp.jar

# ๋ ˆ์ดํ„ด์‹œ ์šฐ์„  ํ™˜๊ฒฝ: ZGC ์กฐํ•ฉ
java \
  -XX:+UseZGC \                          # Generational ZGC (JDK 25์—์„œ ๊ธฐ๋ณธ๊ฐ’)
  -XX:+UseCompactObjectHeaders \          # ๊ฐ์ฒด ํ—ค๋” ์ตœ์ ํ™”
  -Xms4g -Xmx8g \
  -jar myapp.jar
ShellScript

-XX:+UseCompactObjectHeaders๋Š” JDK 25์—์„œ ์‹คํ—˜ ๋”ฑ์ง€๊ฐ€ ๋–ผ์ธ ์ •์‹ ๊ธฐ๋Šฅ์ด๋‹ค. G1๊ณผ ZGC ๋ชจ๋‘์™€ ํ˜ธํ™˜๋˜๊ณ , ๋ณ„๋„ ํž™ ํŠœ๋‹ ์—†์ด๋„ GC ๋กœ๊ทธ์—์„œ ๋ฉ”๋ชจ๋ฆฌ ๊ฐ์†Œ ํšจ๊ณผ๋ฅผ ๋ฐ”๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ธฐ์กด ์ฝ”๋“œ์˜ Double-Checked Locking์„ Stable Values๋กœ ์ „ํ™˜

// Before (JDK 21): ์ด์ค‘ ํ™•์ธ ์ž ๊ธˆ โ€” JIT ์ƒ์ˆ˜ ํด๋”ฉ ๋ถˆ๊ฐ€, volatile ํ•„์ˆ˜
public class MetricsRegistry {
    private static volatile MeterRegistry instance;

    public static MeterRegistry getInstance() {
        if (instance == null) {
            synchronized (MetricsRegistry.class) {
                if (instance == null) {
                    // volatile ํ•„๋“œ๋ผ JIT์ด ์ƒ์ˆ˜๋กœ ์ฒ˜๋ฆฌ ๋ถˆ๊ฐ€
                    instance = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
                }
            }
        }
        return instance;
    }
}

// After (JDK 25): Stable Values โ€” ์ง€์—ฐ ์ดˆ๊ธฐํ™” + JIT ์ƒ์ˆ˜ ํด๋”ฉ ๋™์‹œ ์ ์šฉ
public class MetricsRegistry {
    // static final๋กœ ์„ ์–ธํ•˜๋ฉด JIT์ด ์™„์ „ํ•œ ์ƒ์ˆ˜ ํด๋”ฉ ์ตœ์ ํ™” ์ ์šฉ
    private static final StableValue<MeterRegistry> INSTANCE = StableValue.of();

    public static MeterRegistry getInstance() {
        return INSTANCE.orElseSet(
            () -> new PrometheusMeterRegistry(PrometheusConfig.DEFAULT)
        );
    }
}
Java

Double-Checked Locking์„ StableValue.of()๋กœ ๊ต์ฒดํ•˜๋ฉด ์ฝ”๋“œ๊ฐ€ ์งง์•„์ง€๊ณ , ์ดˆ๊ธฐํ™” ์ดํ›„์—๋Š” JIT์ด ์ƒ์ˆ˜๋กœ ์ธ์‹ํ•ด ์„ฑ๋Šฅ๋„ ์ข‹์•„์ง„๋‹ค. volatile๊ณผ synchronized๊ฐ€ ์‚ฌ๋ผ์ง€๋ฉด์„œ ์ฝ”๋“œ์˜ ์˜๋„๋„ ํ›จ์”ฌ ์ฝ๊ธฐ ์‰ฌ์›Œ์ง„๋‹ค.


๋งˆ์น˜๋ฉฐ

์ง€๊ธˆ๊นŒ์ง€ JDK 25 ์„ฑ๋Šฅ ๊ฐœ์„  ์‚ฌํ•ญ์— ๋Œ€ํ•ด์„œ ์ •๋ฆฌํ•ด ๋ณด์•˜๋‹ค. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(String hashCode 8๋ฐฐ, Memory Segment 2๋ฐฐ), JIT(C2 ์ž๋™ ๋ฒกํ„ฐํ™” 33๋ฐฐ, ๋งˆ์Šคํฌ ์ฒดํฌ 10,000๋ฐฐ), GC(ํž™ 20% ์ ˆ๊ฐ, G1 Remembered Set 60% ๊ฐ์†Œ)๋ผ๋Š” ์„ธ ๋ ˆ์ด์–ด๊ฐ€ ๋™์‹œ์— ๊ฐœ์„ ๋˜์—ˆ๊ณ , Timefold ๋ฒค์น˜๋งˆํฌ์—์„œ ์ฝ”๋“œ ๋ณ€๊ฒฝ ์—†์ด JDK 21 ๋Œ€๋น„ ์•ฝ 8% ์ฒ˜๋ฆฌ๋Ÿ‰์ด ํ–ฅ์ƒ๋˜์—ˆ๋‹ค. JDK 21 LTS๋ฅผ ์šด์˜ ์ค‘์ด๋ผ๋ฉด, pom.xml ๋ฒ„์ „ ๋ฒˆํ˜ธ ํ•˜๋‚˜์™€ -XX:+UseCompactObjectHeaders ํ”Œ๋ž˜๊ทธ ํ•˜๋‚˜๊ฐ€ ์ง€๊ธˆ ๋‹น์žฅ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ€์žฅ ํšจ์œจ์ ์ธ ์„ฑ๋Šฅ ํŠœ๋‹์ด๋‹ค.

์ฐธ๊ณ  ์ž๋ฃŒ