์ด๋ฒ ํฌ์คํ ์์๋ 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());
}
}JavaorElseSet()์ ์ฒซ ํธ์ถ ์๋ง ๋๋ค๋ฅผ ์คํํ๊ณ ์ดํ์๋ ์บ์๋ ๊ฐ์ ๋ฐํํ๋ค. ๋ฉํฐ์ค๋ ๋ ํ๊ฒฝ์์๋ ์ด๊ธฐํ๋ ์ต๋ ํ ๋ฒ๋ง ๋ณด์ฅ๋๊ณ , ์ด๊ธฐํ๊ฐ ๋๋๋ฉด 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
);
}
}JavaStableValue.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); // ์ฒ์ ์ ๊ทผ ์๋ง ์ด๊ธฐํ
}
}JavaStableValue.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>XMLMaven ์ปดํ์ผ๋ฌ ํ๋ฌ๊ทธ์ธ์ --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.jarShellScriptํ๋ จ ์คํ(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'])
}GroovyMaven๊ณผ 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.jarShellScript-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)
);
}
}JavaDouble-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 ํ๋๊ทธ ํ๋๊ฐ ์ง๊ธ ๋น์ฅ ํ ์ ์๋ ๊ฐ์ฅ ํจ์จ์ ์ธ ์ฑ๋ฅ ํ๋์ด๋ค.
์ฐธ๊ณ ์๋ฃ
- Java Performance Update: From JDK 21 to JDK 25 โ Inside.java
- JDK 25 Performance Improvements โ Inside.java
- JEP 502: Stable Values (Preview) โ OpenJDK
- How Fast is Java 25 โ Timefold
- JEP 502 Stable Values Explained โ SoftwareMill
- A Java Champion’s Guide to JDK 25 Features โ Azul
