Java 虚拟机的下一个重要节点正在逼近:Project Valhalla 终于要落地了。
6 月 15 日,Oracle 工程师 Lois Foltan 确认,JEP 401(值类型与对象)将合并入 OpenJDK 主仓库,目标版本是 JDK 28。改动规模之大,让团队不得不请其他提交者暂缓大型代码提交——单个 Pull Request 增加了超过 19.7 万行代码、涉及 1816 个文件。
不过,先别急着庆祝。Brian Goetz(Oracle 首席 Java 架构师)很快泼了盆冷水:这只是 Valhalla 的“第一部分”,功能默认为关闭状态,仍属预览阶段。他还有个经典观察:之前那批坚持“Valhalla 永远不会发布”的人,现在会无缝切换到“这并不是最重要的部分”。社区里流传多年的玩笑是:我们会先走进北欧神话中的英灵殿(Valhalla),而不是等来这个 Valhalla 项目正式发布。
为什么 Java 需要值类型
Valhalla 的口号从一开始就是:“写起来像类,用起来像 int。”这句话点明了整个项目的核心诉求:让开发者能用正常的、具备方法、构造校验和清晰字段名的类来编程,同时让 JVM 能像处理原始类型(int、double 等)那样高效地处理它们。
要理解这个需求,得回到 Java 的底层设计。Java 中除了 8 种原始类型外,一切皆是引用类型。当你写下 Point p = new Point(1, 2),变量 p 并不是一个点——它是一张写着地址的纸条,指向堆上某处真正存放的对象。每次访问字段,JVM 都得先顺着指针“查地址”。单个对象无所谓,但规模一大问题就来了:每个堆上的对象都有自己独立的 Header(包含类型信息、锁状态等,约十几字节);每个对象都需单独分配和回收;如果一个数组存放一百万个 Point,实际上是在堆里撒了一百万张纸条。Brian Goetz 将这种内存布局形容为“fluffy”——松软膨胀的。
硬件变了,Java 没跟上
密度为什么重要?因为硬件的变化速度远超 Java 的演进。1995 年,内存访问和 CPU 运算的成本大致相当;今天 CPU 比主内存快约两个数量级,中间全靠缓存填充。CPU 按缓存行(通常 64 字节)批量读取内存:如果数据紧密连续排列,一次缓存填充能带来大量有用值;如果要跳着查指针,每次访问都可能触发缓存未命中,速度可差上百倍。这就是“引用局部性”——整场优化的真正赌注。
有人会提到 JIT 的逃逸分析(escape analysis):JVM 能识别某些对象从未“逃出”局部代码范围,于是根本不分配它,对象字段直接散开进寄存器或局部变量。这听起来很美,但问题在于优化结果不可预测且脆弱——只要对象被存入另一个类的字段、放进数组、传入更复杂的方法,或超出 JIT 分析边界,整个技巧就失效。源码没变,性能行为却可能天差地别。这也是资深 JVM 工程师把逃逸分析当“意外惊喜”而非项目基石的原因。
正因如此,游戏引擎、图形库、图像处理、数据库、分析引擎和高性能计算领域长期采用“硬编码”方案:用原始字节而非对象来存储数据(如颜色直接存 r、g、b 三个字节)。速度换来了,但牺牲了安全性与可读性——不再有命名、私有状态、校验和方法。JEP 401 正是要终结这个两难困境。
编注:信源为 JVM Weekly 周刊(jvm-weekly.com),文章为深度技术解读,侧重 Valhalla 理念、内存模型问题与硬件背景;Oracle 工程师 Lois Foltan 与 Brian Goetz 均有直接引语。材料覆盖项目完整动机与现状,未深入展开 JDK 28 后续预览阶段的 API 细节。