10년을 기다린 자바의 대수술, 발할라가 드디어 옵니다
자바를 한 번이라도 진지하게 써본 분이라면 “Project Valhalla(발할라 프로젝트)“라는 이름을 들어보셨을 겁니다. 2014년에 시작된 이 프로젝트는 자바 진영에서 “언젠가는 온다"는 전설처럼 회자돼 왔는데요. 그 언젠가가 드디어 JDK 28에서 첫 결실을 맺습니다. 무려 10년 넘게 걸린 자바의 대수술, 도대체 뭘 고치겠다고 이렇게 오래 걸린 걸까요.
자바의 오래된 숙제, “모든 게 객체다"의 대가
자바를 배울 때 가장 먼저 듣는 말이 있습니다. “자바에서는 거의 모든 것이 객체다.” 이 철학은 자바를 깔끔하고 일관성 있게 만들어줬습니다. 하지만 성능 측면에서는 두고두고 발목을 잡아왔는데요.
문제의 핵심은 객체 헤더와 포인터 간접 참조입니다. 자바에서 객체를 하나 만들면, 실제 데이터 외에 추가로 따라붙는 살림살이가 있습니다. 객체의 정체를 식별하는 헤더, 그리고 그 객체가 메모리 어디에 있는지 가리키는 포인터입니다.
예를 들어 좌표 하나를 표현하는 Point(x, y) 객체를 100만 개 만든다고 해봅시다. 우리가 정말 원하는 건 숫자 두 개뿐인데, 자바는 객체마다 헤더를 붙이고 메모리 곳곳에 흩어놓은 뒤 포인터로 일일이 찾아갑니다. 데이터는 작은데 포장지가 더 큰 셈입니다.
이게 왜 문제냐면, CPU는 메모리를 한 줄로 쭉 읽을 때 가장 빠르거든요. 데이터가 여기저기 흩어져 있으면 CPU가 매번 “그게 어디 있더라” 하고 찾아다녀야 합니다. 이걸 포인터 추적(pointer chasing)이라고 부르는데, 현대 하드웨어에서 성능을 갉아먹는 대표적인 주범입니다.
발할라의 해법, “값처럼 행동하는 객체”
발할라가 들고 온 핵심 무기는 값 객체(Value Objects)입니다. 정식 명칭은 JEP 401, “Value Classes and Objects"인데요. 개념은 의외로 직관적입니다.
기존 객체는 “정체성(identity)“을 가집니다. 내용이 똑같은 두 객체라도 서로 다른 메모리 주소에 있으면 다른 객체로 취급되죠. 반면 값 객체는 정체성을 포기합니다. 내용만 같으면 같은 것으로 봅니다. 마치 숫자 5와 5가 어디에 있든 그냥 같은 5인 것처럼요.
정체성을 포기하면 무슨 일이 일어날까요. JVM이 자유를 얻습니다. 객체 헤더를 떼어버릴 수 있고, 포인터로 가리키는 대신 데이터를 메모리에 통째로 깔아버릴 수 있습니다. 흩어진 객체들을 하나의 배열에 빽빽하게 채워 넣는 게 가능해지는 거죠. 이걸 흔히 “flattening(평탄화)“이라고 부릅니다.
개발자가 쓰는 코드는 거의 그대로입니다. 클래스 앞에 value 키워드를 붙이는 정도인데요. 코드 모양은 평범한 객체와 똑같지만, 속에서는 가벼운 데이터 덩어리처럼 동작합니다. “객체의 편안함은 그대로, 원시 타입의 속도를 더하자"가 발할라의 슬로건인 셈입니다.
JDK 28에서 실제로 도착하는 것들
여기서 한 가지 솔직하게 짚고 넘어가야 할 부분이 있습니다. 발할라는 워낙 거대한 프로젝트라 한 번에 다 들어오지 않습니다. 여러 JEP로 쪼개서 단계적으로 들어오는데요.
JDK 28 단계에서 주목받는 건 값 객체의 기반을 까는 작업들입니다. 값 클래스 문법, 그리고 이미 우리가 쓰던 Integer, Double 같은 박싱 타입들을 값 기반으로 슬쩍 바꾸는 작업이 포함됩니다. 기존 코드를 거의 건드리지 않고도 성능 이득을 보게 만드는 영리한 전략입니다.
그다음 단계로는 null을 허용하지 않는 값 타입, 그리고 제네릭에 원시 타입을 직접 넣는 작업이 줄지어 대기 중입니다. 지금은 List<int>를 못 쓰고 List<Integer>로 돌아가야 하는데, 이 답답한 제약이 결국 사라지는 방향입니다.
다만 기대치는 현실적으로 잡으시는 게 좋습니다. 발할라는 “이번 버전에서 끝!“이 아니라 “이번 버전부터 시작"에 가깝습니다. 진짜 폭발적인 성능 향상은 여러 단계가 다 맞물린 뒤에 체감될 가능성이 높습니다.
AI와 고성능 시대에 왜 하필 지금일까
그럼 왜 하필 지금 이 오래된 숙제가 다시 주목받을까요. 시대가 바뀌었기 때문입니다.
요즘 소프트웨어가 다루는 데이터의 양은 10년 전과 차원이 다릅니다. AI 모델은 수십억 개의 숫자(벡터)를 메모리에서 끊임없이 굴립니다. 추천 시스템, 실시간 분석, 대규모 시뮬레이션도 마찬가지입니다. 이런 작업에서는 “데이터를 얼마나 촘촘하게 메모리에 깔고 얼마나 빠르게 훑느냐"가 곧 성능이자 비용입니다.
그동안 이 영역에서 자바는 C++나 러스트, 혹은 파이썬+C 조합에 자리를 내주는 경우가 많았습니다. 객체 오버헤드 때문에 숫자 연산이 무거웠으니까요. 발할라는 바로 이 약점을 정조준합니다. 자바도 메모리를 빽빽하게 다루고 숫자를 빠르게 굴릴 수 있게 만들어, AI와 데이터 집약적 워크로드 영역에서 다시 경쟁력을 갖추겠다는 겁니다.
흥미로운 점은 이게 단순히 자바 한 언어만의 이야기가 아니라는 겁니다. 카프카, 스파크, 일래스틱서치처럼 데이터 인프라의 핵심을 떠받치는 거대한 시스템들이 JVM 위에서 돌아갑니다. 발할라가 안착하면 이 생태계 전체가 별다른 코드 수정 없이 더 적은 메모리로 더 빠르게 돌아갈 잠재력이 생깁니다. 밑바닥을 바꾸면 그 위에 올라탄 모두가 혜택을 보는 구조입니다.
10년의 무게를 어떻게 봐야 할까
10년이 걸렸다는 건 두 가지로 읽힙니다. 하나는 “그만큼 어려운 문제였다"는 것이고, 다른 하나는 “그만큼 신중했다"는 것입니다.
발할라 팀이 시간을 끈 건 게을러서가 아닙니다. 기존에 작동하던 수십억 줄의 자바 코드를 단 한 줄도 깨뜨리지 않으면서 언어의 근본을 바꾸는, 정말 까다로운 줄타기를 해왔기 때문입니다. 자바의 가장 큰 자산인 하위 호환성을 지키느라 그만큼 돌다리를 두드린 거죠. 화려한 신기능을 빨리 내놓는 것보다, 기존 자산을 지키는 걸 우선한 자바다운 선택입니다.
JDK 28의 첫 발걸음은 작아 보일 수 있습니다. 하지만 방향은 분명합니다. 자바는 “느리지만 안전한 언어"라는 오래된 꼬리표를 떼어내려 하고 있습니다. 여러분의 코드가 어느 날 똑같은 모습으로도 훨씬 빨라진다면, 그건 발할라가 조용히 일하고 있다는 신호일 겁니다. 10년을 기다린 이 대수술, 과연 자바의 다음 10년을 바꿔놓을 수 있을까요.
더 깊이 들여다보기
댓글
댓글을 불러오는 중...