요약
Java를 오래 써온 입장에서 Project Valhalla는 단순한 문법 추가로 보이지 않습니다. 솔직히 말하면 기대가 섞여 있습니다. 20년 가까이 Java를 메인으로 써왔고, 여전히 Java의 안정감과 생태계를 좋아합니다. 그래서 요즘 Python, Go, Rust를 쓰면서도 한편으로는 “Java도 이 부분이 조금 더 좋아졌으면 좋겠다”는 마음이 계속 남아 있었습니다.
요즘 AI 덕분에 Python을 더 자주 쓰고, 최근에는 Go와 Rust도 사용했습니다. AI가 코드를 꽤 잘 받쳐주는 것도 이유였지만, 결국 속도와 메모리, 배포의 가벼움도 무시하기 어려웠습니다. Valhalla가 들어온다고 Java가 갑자기 Rust처럼 바뀌는 것은 아닙니다. 그래도 value class, identity 없는 객체, flattening, primitive와 class 모델의 통합은 Java가 다시 성능 민감한 영역에서 더 설득력 있는 선택지가 될 가능성을 만듭니다.
목차
이 글에서 다루는 내용
배경: 왜 다시 Java 성능 이야기를 하게 됐나
저는 약 20년 동안 Java를 메인 언어로 써왔습니다. 엔터프라이즈 백엔드, 배치, API 서버, 운영 시스템을 만들다 보면 Java만큼 안정적인 선택지도 많지 않았습니다. 생태계는 크고, JVM은 성숙했고, Spring을 비롯한 프레임워크도 강합니다. 오래 쓰다 보니 단순히 익숙한 정도를 넘어서, Java가 가진 균형감 자체를 꽤 좋아하게 됐습니다.
물론 좋아한다고 해서 아쉬움이 없는 것은 아닙니다. 오히려 오래 썼기 때문에 더 잘 보이는 아쉬움이 있습니다. 작은 객체가 많이 생기는 구조에서의 메모리 부담, primitive와 객체 세계가 갈라져 있는 느낌, 성능 때문에 도메인 모델을 포기해야 하는 순간들이 그렇습니다. Java를 좋아하는 입장에서는 이런 부분이 개선되었으면 하는 마음이 자연스럽게 생깁니다.
그런데 최근 몇 년은 상황이 조금 달라졌습니다. AI 도구가 코드를 꽤 잘 받쳐주면서 Python을 쓰는 부담이 줄었습니다. 예전 같으면 타입 안정성이나 구조화 때문에 Java를 고집했을 일도, 이제는 AI 도움을 받아 Python으로 빠르게 실험하고 검증하는 일이 많아졌습니다.
최근에는 Go와 Rust도 더 많이 보게 됐습니다. 이유는 단순합니다. 속도와 배포, 메모리 사용량, 컨테이너 환경에서의 가벼움 때문입니다. 특히 Go는 서버를 작고 단순하게 만들기 좋고, Rust는 성능과 안정성을 강하게 가져갈 수 있습니다.
그렇다면 Java는 이제 “무겁지만 안정적인 레거시 선택지”로 남는 걸까요? 저는 Project Valhalla가 이 질문에 대한 꽤 중요한 답이 될 수 있다고 봅니다. 그리고 솔직히는, Java를 오래 써온 사람으로서 그 답이 긍정적이었으면 좋겠습니다. 완전히 새로운 언어로 갈아타는 것보다, 좋아하던 Java가 현대적인 성능 요구에 조금 더 잘 맞게 바뀌는 쪽이 더 반갑기 때문입니다.
Java Project Valhalla가 해결하려는 성능 문제
Java의 큰 장점은 객체 모델입니다. 클래스, 인터페이스, 캡슐화, 다형성 덕분에 큰 시스템을 구조화하기 좋습니다. 문제는 모든 것을 객체로 표현할 때 비용이 생긴다는 점입니다.
예를 들어 Money, Point, Range, Color, EventTime, UserId 같은 작은 값 객체를 생각해볼 수 있습니다. 도메인 모델에서는 이런 타입을 만드는 것이 좋습니다. long amount 하나보다 Money amount가 훨씬 읽기 쉽고 실수를 줄입니다.
하지만 JVM 입장에서는 이런 객체들이 대량으로 생성될 때 비용이 생깁니다.
- 객체마다 identity가 있다.
- heap allocation이 필요하다.
- 참조를 따라가야 한다.
- 배열에 넣으면 값이 아니라 reference 배열이 된다.
- CPU cache locality가 나빠질 수 있다.
- GC 부담이 커질 수 있다.
결국 개발자는 두 선택지 사이에서 자주 고민합니다.
도메인 모델을 깔끔하게 만들 것인가?
성능을 위해 primitive와 배열 중심으로 납작하게 만들 것인가?
실무에서는 이 타협이 자주 나옵니다. 처음에는 객체지향적으로 잘 만들었다가, 성능 문제가 생기면 long, int, double[], LongStream, custom buffer 같은 방식으로 내려갑니다. 그러면 코드는 빨라질 수 있지만, 도메인 의미는 흐려집니다.(아니, 사실 B2B는 도메인이지!라고 자위하며 유지보수떄문에 객체지향적으로 더 가는게 있습니다...)
Project Valhalla는 이 오래된 간극을 줄이려는 프로젝트입니다. OpenJDK Valhalla 페이지도 목표를 “Java object model에 value objects를 추가해 객체지향 추상화와 primitive 성능 특성을 결합하는 것”으로 설명합니다.
Java value class란 무엇인가: identity 없는 객체
JEP 401의 핵심은 Value Classes and Objects입니다. 요지는 간단합니다.
값만 중요하고 identity는 중요하지 않은 객체를 Java가 직접 표현할 수 있게 하자.
일반 객체는 identity가 있습니다. 같은 값을 가진 객체라도 서로 다른 객체일 수 있습니다.
new Point(1, 2) != new Point(1, 2)
지금 Java의 일반 객체 모델에서는 “값이 같다”와 “같은 객체다”가 다릅니다. equals()는 같을 수 있지만 ==는 다릅니다.
Valhalla의 value object는 이 전제를 바꿉니다. identity가 없고, 객체의 상태값으로만 구분됩니다. JEP 401 예시에서는 이런 형태가 나옵니다.
value record Point(int x, int y) {}
Point p = new Point(17, 3);
Objects.hasIdentity(p); // false
new Point(17, 3) == p; // true
이 변화가 중요한 이유는 JVM이 더 공격적으로 최적화할 수 있기 때문입니다. identity가 없으면 객체를 반드시 heap 위의 “특정 위치에 있는 무언가”로 다룰 필요가 줄어듭니다. JVM은 상황에 따라 객체를 flatten하거나 scalarize할 수 있습니다.
쉽게 말하면 이런 방향입니다.
기존 객체:
배열 → 참조 → heap 객체 → 필드
value object 최적화 가능 구조:
배열 → 필드 값이 바로 배치될 가능성
이 차이는 작은 객체가 아주 많이 쌓이는 코드에서 중요합니다.
Java 성능 개선에 Project Valhalla가 도움이 될까
희망은 있습니다. 다만 “모든 Java 코드가 갑자기 빨라진다”는 식으로 기대하면 안 됩니다. 오래 Java를 써온 입장에서 더 기대하는 것은 극적인 한 방보다, 그동안 계속 아쉬웠던 지점들이 조금씩 구조적으로 좋아지는 변화입니다.
Valhalla가 성능상 기대되는 이유는 크게 세 가지입니다.
1. 작은 객체의 allocation 비용을 줄일 수 있다
Java 백엔드에서는 작은 불변 객체가 계속 만들어집니다. 요청 처리 중 DTO, record, domain value, tuple-like object, event object가 생성됩니다. JIT와 escape analysis가 일부 최적화해주지만, 항상 기대대로 되는 것은 아닙니다.
value class는 JVM이 이런 객체를 더 값처럼 다룰 수 있는 길을 열어줍니다. 객체 identity가 필요 없으니, JVM은 heap allocation을 피하거나 필드 단위로 풀어낼 여지가 커집니다.
2. 메모리 layout이 좋아질 수 있다
지금 Java에서 Point[]는 사실상 Point 객체 참조들의 배열입니다. 각 Point 객체는 heap 어딘가에 따로 있을 수 있습니다. 데이터가 흩어지면 CPU cache 관점에서 불리합니다.
Valhalla의 방향은 value object를 필드나 배열 안에 더 compact하게 배치할 수 있게 하는 것입니다. OpenJDK 문서에서는 flattening과 scalarization을 중요한 최적화 포인트로 설명합니다.
성능은 결국 CPU가 데이터를 얼마나 연속적으로 잘 읽느냐와도 관련이 있습니다. Go나 Rust가 강하게 느껴지는 이유 중 하나도 데이터 layout을 더 직접적으로 통제하기 쉽기 때문입니다. Valhalla는 Java가 이 영역에서 조금 더 가까워지려는 시도입니다.
3. 도메인 모델과 성능 사이의 타협을 줄일 수 있다
이 부분이 저는 가장 중요하다고 봅니다. Java 개발자는 성능 때문에 객체 모델을 포기하는 순간이 있습니다.
예를 들어 Money, Coordinate, TimeRange 같은 타입은 도메인 모델상으로는 객체가 맞습니다. 그런데 성능 때문에 primitive로 풀어헤치면 코드의 의미가 약해집니다.
Valhalla가 성공하면 이런 식의 선택지가 생깁니다.
value class Money {
private long cents;
public Money(long cents) {
this.cents = cents;
}
public long cents() {
return cents;
}
}
이런 타입이 실제 런타임에서 더 값에 가깝게 최적화될 수 있다면, 개발자는 코드의 의미를 유지하면서도 성능 손해를 줄일 수 있습니다.
Java 개발자에게 Project Valhalla가 중요한 이유
20년 가까이 Java를 써온 입장에서 Valhalla가 중요한 이유는 “Java가 다시 최신 언어처럼 보인다”가 아닙니다. 더 본질적인 이유는 Java가 자신의 강점을 유지한 채 약점을 줄이려 하기 때문입니다.
Java의 강점은 여전히 큽니다.
- 큰 시스템을 유지보수하기 좋다.
- JVM 생태계가 안정적이다.
- 운영 도구와 관측 도구가 성숙하다.
- 라이브러리와 프레임워크가 풍부하다.
- 팀 개발과 장기 운영에 강하다.
하지만 약점도 분명합니다.
- 메모리 사용량이 크다고 느껴질 때가 많다.
- 작은 객체가 많은 코드에서 GC 부담이 생긴다.
- primitive와 generic/object 세계가 어색하게 나뉘어 있다.
- 성능 민감 코드에서는 데이터 layout을 직접 다루기 어렵다.
Go와 Rust를 쓰다 보면 이 차이가 체감됩니다. Go는 단순한 바이너리와 빠른 시작, 쉬운 동시성 모델이 매력적입니다. Rust는 성능과 메모리 안정성을 강하게 가져갑니다. Python은 AI 생태계와 실험 속도에서 압도적입니다.
그런데 Java가 Valhalla를 통해 작은 값 객체와 메모리 layout 문제를 개선한다면, Java는 다시 이런 포지션을 강화할 수 있습니다.
대규모 시스템의 안정성
+ 도메인 모델의 표현력
+ JVM 운영 생태계
+ 이전보다 나아진 값 타입 성능
이 조합은 여전히 강합니다. 그래서 Valhalla를 보면서 드는 감정은 단순한 신기술 관심보다 조금 더 개인적입니다. 오래 써온 Java가 아직도 앞으로 나아가고 있고, 성능 때문에 다른 언어를 선택해야 했던 일부 상황을 다시 가져올 수 있지 않을까 하는 기대입니다.
주의할 점: 아직 만능 해결책은 아니다
Valhalla를 볼 때 조심해야 할 부분도 있습니다.
1. JEP 401은 Preview 단계다
OpenJDK JEP 401은 Value Classes and Objects (Preview)입니다. JVM Weekly 글에서는 JEP 401이 main OpenJDK repository에 통합되고 JDK 28을 target으로 한다고 설명하지만, preview 기능이라는 점이 중요합니다.
즉 실무 프로덕션 코드에 바로 전면 도입할 단계라기보다는, 언어와 VM의 방향을 확인하고 실험하는 단계로 보는 것이 맞습니다.
2. Generics 문제가 한 번에 해결되는 것은 아니다
많은 Java 개발자가 기대하는 것은 List<int> 같은 primitive generic의 성능 개선입니다. JEP 402는 enhanced primitive boxing을 다루지만, 문서상으로도 현재 generics는 여전히 erasure 기반입니다. List<int>가 곧바로 int[]처럼 빠르게 동작하는 식의 변화는 아닙니다.
OpenJDK JEP 402는 future JVM enhancements가 primitive parameterization에 대한 specialized optimization을 가능하게 할 수 있다고 설명합니다. 즉 이쪽은 Valhalla의 큰 방향 안에 있지만, 한 번에 끝나는 문제는 아닙니다.
3. 기존 객체 모델과 다른 직관이 필요하다
identity가 없다는 것은 장점이지만, 동시에 개발자가 조심해야 할 부분입니다.
- synchronization 대상이 될 수 없다.
==의미가 달라진다.- identity 기반 자료구조나 캐싱과 맞지 않을 수 있다.
- mutable object처럼 생각하면 안 된다.
JEP 401도 ==, synchronized 같은 기존 동작에서 개발자가 놀랄 수 있음을 위험 요소로 언급합니다.
실무에서 먼저 볼 만한 적용 지점
Valhalla가 안정화되면 저는 다음 영역부터 먼저 볼 것 같습니다.
1. 도메인 value object
Money
UserId
OrderId
Coordinate
TimeRange
Version
이런 타입은 의미가 중요하지만, 성능 때문에 primitive로 풀어버리고 싶은 유혹이 있습니다. value class가 잘 자리 잡으면 가장 자연스러운 적용 대상입니다.
2. 대량 이벤트/로그/메트릭 데이터
이벤트 스트림, 로그 처리, 메트릭 집계처럼 작은 데이터 구조가 대량으로 흐르는 곳에서도 기대할 수 있습니다.
EventTime
MetricPoint
TraceId
SpanRange
CounterValue
이런 구조가 heap object reference의 집합이 아니라 더 compact한 값처럼 다뤄질 수 있다면 GC와 cache locality 측면에서 이점이 생길 수 있습니다.
3. 수치 계산과 좌표/벡터 데이터
Java가 AI 학습의 주류 언어가 되기는 어렵겠지만, 서버 쪽에서 벡터 검색, 추천, 랭킹, 좌표 계산, 금융 계산 같은 일을 할 때는 value class의 의미가 있습니다.
특히 Java 진영에서 Panama, Vector API, Valhalla가 함께 성숙하면 “JVM 위에서 성능 민감 데이터를 다루는 방식”이 지금보다 좋아질 수 있습니다.
결론
Project Valhalla는 Java 개발자에게 꽤 오래 기다린 변화입니다. 20년 가까이 Java를 써온 입장에서 보면, Java의 문제는 문법이 낡았다는 것보다 객체 모델과 성능 모델 사이의 간극이었습니다.
저는 Java를 오래 썼고, 여전히 좋아합니다. 큰 시스템을 만들고 오래 운영할 때 Java가 주는 안정감, JVM 생태계의 성숙함, 팀 단위 개발에서의 예측 가능성은 쉽게 버리기 어렵습니다. 그래서 요즘 Python, Go, Rust를 쓰면서도 마음 한쪽에는 “Java가 이 부분만 조금 더 좋아지면 좋을 텐데”라는 생각이 남아 있었습니다.(아무래도 익숙한게 좋으니까요)
우리는 좋은 도메인 모델을 만들고 싶지만, 성능이 중요한 순간에는 primitive와 배열, buffer, 별도 최적화 코드로 내려가야 했습니다. Valhalla는 이 간극을 줄이려는 시도입니다.
물론 아직 조심해야 합니다. JEP 401은 Preview이고, generics 성능 문제가 한 번에 해결되는 것도 아닙니다. Go나 Rust가 가진 장점이 사라지는 것도 아닙니다. Java가 갑자기 모든 영역에서 다시 최선의 선택지가 된다고 말할 수도 없습니다.
그래도 저는 Valhalla를 Java 성능 개선의 현실적인 희망으로 봅니다. Java가 갑자기 다른 언어가 되는 것이 아니라, Java다운 방식으로 성능의 바닥을 조금 더 끌어올리는 변화이기 때문입니다.
오래 쓴 언어가 좋아지는 모습을 보는 건 생각보다 반가운 일입니다. Java가 과거의 선택지가 아니라, 여전히 개선되고 있는 현재의 플랫폼이라는 점을 Valhalla가 보여줬으면 합니다. 요즘 AI 덕분에 Python, Go, Rust를 더 쉽게 오가게 됐지만, 장기 운영 시스템에서 Java가 가진 안정감은 여전히 큽니다. 여기에 value object와 더 나은 memory layout이 붙는다면, Java는 다시 “무겁지만 익숙한 언어”가 아니라 “큰 시스템을 오래 운영하면서도 성능 타협을 줄일 수 있는 언어”로 평가받을 수 있습니다.
일단 나오고 나면 이리저리 테스트를 진행해봐야할거 같습니다. 하지만 개인적으로 기대는 되고 있어요.
FAQ
Project Valhalla는 JDK 28에 확정으로 들어오나요?
현재 확인 기준으로 JEP 401은 Value Classes and Objects (Preview) 상태이며, JDK 28을 target으로 한다는 업계 보도가 있습니다. 다만 Preview 기능이므로 최종 사양과 실제 사용 방식은 바뀔 수 있습니다. 프로덕션 전면 도입보다는 실험과 학습 대상으로 보는 것이 안전합니다.
Valhalla가 들어오면 Java가 Go나 Rust만큼 빨라지나요?
그렇게 단순하게 볼 수는 없습니다. Valhalla는 작은 값 객체, 메모리 layout, allocation 비용을 줄이는 방향에서 의미가 큽니다. 하지만 Go의 단순 배포 모델이나 Rust의 ownership 기반 메모리 안정성과는 다른 문제입니다. Java의 강점을 유지하면서 특정 성능 약점을 줄이는 변화로 보는 것이 맞습니다.
기존 record와 value class는 무엇이 다른가요?
record는 데이터 carrier를 간결하게 표현하는 문법입니다. 하지만 일반 record 인스턴스는 여전히 identity를 가진 객체입니다. 반면 value record 또는 value class는 identity 없는 값 객체를 지향합니다. 이 차이 때문에 JVM이 더 compact한 저장과 최적화를 시도할 수 있습니다.
Generics 성능도 바로 개선되나요?
바로 전면 개선된다고 보기는 어렵습니다. JEP 402는 primitive boxing 개선을 다루지만, 문서상 generics는 여전히 erasure 기반이라는 점을 설명합니다. primitive parameterization에 대한 specialized optimization은 Valhalla의 더 큰 방향 안에 있지만, 단계적으로 봐야 합니다.
Java Valhalla 후속
이 글과 이어서 읽을 Java Valhalla 후속 글입니다. 작성 중인 글은 완성되는 대로 링크를 연결합니다.