摘要
从一个长期使用 Java 的开发者角度看,Project Valhalla 并不只是又一个语法特性。说实话,这里面是带着一点期待的。我把 Java 当作主力语言用了将近 20 年,到现在依然喜欢 Java 带来的稳定感和生态。所以即使最近在用 Python、Go、Rust,心里还是一直有一个想法:“如果 Java 在这方面能再好一点就好了。”
最近因为 AI 的帮助,我更多地使用 Python,也开始更多接触 Go 和 Rust。AI 能补上不少开发效率上的问题,这当然是原因之一,但速度、内存、部署轻量化这些问题也确实很难忽视。Valhalla 不会让 Java 一下子变成 Rust。不过,value class、没有 identity 的对象、flattening,以及 primitive 和 class 模型之间更好的衔接,确实有可能让 Java 在性能敏感的场景里重新变得更有说服力。
目录
In this article / 本文内容
为什么又开始关注 Java 性能
我大约 20 年来一直把 Java 作为主要语言。做企业级后端、批处理、API 服务、长期运行的运营系统时,像 Java 这样稳定的选择并不多。生态足够大,JVM 足够成熟,Spring 这样的框架也足够强。用得久了以后,Java 对我来说不只是“熟悉”而已,我也确实喜欢 Java 那种平衡感。
当然,喜欢 Java 不代表没有遗憾。反而因为用得久,有些遗憾会看得更清楚。比如小对象大量产生时的内存负担,primitive 和对象世界之间的割裂感,还有因为性能问题不得不放弃漂亮领域模型的那些时刻。站在一个喜欢 Java 的人的角度,自然会希望这些地方能被改善。
这几年情况有些变化。AI 编程工具让写 Python 的负担小了很多。以前因为类型安全和结构化,我可能会坚持用 Java;但现在借助 AI,用 Python 快速试验和验证也变得容易了很多。
最近我也更多地用 Go 和 Rust。原因很简单:速度、部署、内存使用,以及容器环境里的轻量化。Go 很适合做小而简单的服务,Rust 则在性能和内存安全上很强。
那 Java 以后就只能是“重但稳定的遗留选择”吗?我觉得 Project Valhalla 可能是这个问题的一个重要答案。而且说实话,作为一个用了 Java 很久的人,我希望这个答案是正面的。比起彻底换到一门新语言,我更希望自己一直喜欢的 Java 能更好地适应现代性能需求。
Project Valhalla 想解决什么问题
Java 的一个巨大优势是对象模型。类、接口、封装、多态,让大型系统更容易组织。问题在于,如果所有东西都用对象来表达,就会有成本。
可以想象 Money、Point、Range、Color、EventTime、UserId 这样的小型值对象。在领域模型里,这些类型是很有意义的。相比一个裸的 long amount,Money amount 更清晰,也更不容易犯错。
但从 JVM 的角度看,这些对象大量创建时会带来成本。
- 每个对象都有 identity。
- 需要 heap allocation。
- 程序需要沿着引用访问对象。
- 放进数组时,数组里放的是引用,而不是值本身。
- CPU cache locality 可能变差。
- GC 压力可能增加。
实际开发中,开发者经常会面对这个取舍。
是保持干净的领域模型?
还是为了性能把它压平成 primitive 和数组?
实务中这种取舍经常出现。一开始我们会按面向对象的方式把模型设计得很好,但一旦出现性能问题,就会往 long、int、double[]、LongStream、custom buffer 这些方向下沉。这样代码可能会变快,但领域含义会变弱。不是,实际上在 B2B 里也会一边自我安慰说“这就是领域嘛!”一边因为维护性而更坚持面向对象。
Project Valhalla 就是在尝试缩小这个长期存在的间隙。OpenJDK Valhalla 页面把目标描述为:在 Java object model 中加入 value objects,把面向对象的抽象和简单 primitive 的性能特性结合起来。
核心变化:value class 和没有 identity 的对象
JEP 401 的核心是 Value Classes and Objects。意思很简单:
让 Java 能直接表达那些只关心值、不关心对象 identity 的对象。
普通 Java 对象有 identity。即使两个对象的字段值相同,它们也可以是不同的对象。
new Point(1, 2) != new Point(1, 2)
在现在的 Java 对象模型里,“值相同”和“同一个对象”是两件事。equals() 可能为 true,但 == 不一样。
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 代码突然都变快。作为一个长期使用 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 能更紧凑地放进字段或数组中。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 开发者很重要
从一个用了 Java 将近 20 年的开发者角度看,Valhalla 重要,并不是因为它让 Java 看起来像最新潮的语言,而是因为 Java 正在试图保留自己的强项,同时减少自己的弱点。
Java 的强项依然很明显。
- 适合维护大型系统。
- JVM 生态稳定。
- 运维和可观测性工具成熟。
- 类库和框架丰富。
- 适合团队开发和长期运营。
但弱点也确实存在。
- 内存使用有时显得偏重。
- 小对象很多的代码会增加 GC 压力。
- primitive 和 generic/object 世界割裂得有些别扭。
- 在性能敏感代码里,很难直接控制数据 layout。
用 Go 和 Rust 时,这些差异会很明显。Go 有简单的二进制、快速启动和直接的并发模型。Rust 有强性能和内存安全。Python 则在 AI 生态和快速试验上占据优势。
如果 Java 能通过 Valhalla 改善小型值对象和内存 layout 问题,那么它可以重新强化这样的位置:
大型系统的稳定性
+ 领域模型的表达力
+ JVM 运维生态
+ 比以前更好的值类型性能
这个组合依然很强。所以我看 Valhalla 时,感受不只是对一个新技术特性的兴趣,而是更个人化一点。用了这么久的 Java 还在往前走,而一些过去因为性能不得不选择其他语言的场景,也许未来又可以回到 Java 里。
需要注意的地方:它还不是万能解法
看 Valhalla 时也需要谨慎。
1. JEP 401 仍然是 Preview 阶段
OpenJDK JEP 401 是 Value Classes and Objects (Preview)。JVM Weekly 提到 JEP 401 正在合入 main OpenJDK repository,并以 JDK 28 为目标,但重点是它仍然是 Preview 功能。
也就是说,它还不是应该立刻在生产系统里全面采用的东西。更适合先作为学习、实验和评估对象。
2. Generics 问题不会一次性解决
很多 Java 开发者期待的是类似 List<int> 这样的 primitive generics 性能改进。JEP 402 讨论了 enhanced primitive boxing,但文档也说明当前 generics 仍然基于 erasure。List<int> 不会马上像 int[] 一样快。
JEP 402 提到,未来 JVM enhancements 可能让 primitive parameterization 获得 specialized performance optimizations。所以这是 Valhalla 大方向的一部分,但不是一步到位的奇迹。
3. 需要适应不同的对象直觉
没有 identity 是优点,但也意味着开发者需要小心。
- 不能把 value object 用作 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 的集合,而是能更紧凑地处理,就可能在 GC 和 cache locality 上获得收益。
3. 数值、坐标和向量类数据
Java 不一定会成为 AI 训练的主流语言,但在服务端做向量搜索、推荐、排序、坐标计算、金融计算时,value class 仍然有意义。
如果 Java 阵营里的 Panama、Vector API 和 Valhalla 一起成熟,那么 JVM 上处理性能敏感数据的方式,可能会比现在好很多。
结论
Project Valhalla 是 Java 开发者等待了很久的变化。从一个用了 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 能说明,Java 不是过去的选择,而仍然是一个在继续改进的平台。现在 AI 让我们更容易在 Python、Go、Rust 之间切换,但 Java 在长期运行系统里的稳定感依然很强。如果再加上 value object 和更好的 memory layout,Java 也许就不只是“重但熟悉的语言”,而是“能长期运行大型系统,同时减少性能取舍的语言”。
等它真正出来以后,应该还是要各种测试一下。但我个人是有期待的。
FAQ
Project Valhalla 已经确定会进入 JDK 28 吗?
以本文确认的资料为准,JEP 401 是 Value Classes and Objects (Preview),并且有业界报道说它以 JDK 28 为目标。但因为它是 Preview 功能,最终规格和实际使用方式仍可能变化。更安全的做法,是先把它当作实验和学习对象,而不是马上大规模用于生产。
Valhalla 会让 Java 像 Go 或 Rust 一样快吗?
不能这么简单地看。Valhalla 对小型值对象、内存 layout、allocation 成本这些方面很有意义。但 Go 的简单部署模型和 Rust 的 ownership 内存安全解决的是不同问题。Valhalla 更适合被理解为:Java 在保留自身强项的同时,减少一个长期存在的性能弱点。
现有 record 和 value class 有什么不同?
record 是简洁表达 data carrier 的语法。但普通 record 实例仍然是有 identity 的对象。value record 或 value class 则是面向没有 identity 的值对象。正是这个差异,让 JVM 可以尝试更紧凑的存储和优化。
Generics 性能会马上改善吗?
不会马上全面改善。JEP 402 讨论了 primitive boxing 改进,但文档也说明 generics 仍然基于 erasure。针对 primitive parameterization 的 specialized optimization 是 Valhalla 更大方向的一部分,但应该分阶段看待。
参考资料
- OpenJDK Project Valhalla: https://openjdk.org/projects/valhalla/
- JEP 401: Value Classes and Objects (Preview): https://openjdk.org/jeps/401
- JEP 402: Enhanced Primitive Boxing (Preview): https://openjdk.org/jeps/402
- State of Valhalla: Part 1, The Road to Valhalla: https://openjdk.org/projects/valhalla/design-notes/state-of-valhalla/01-background
- State of Valhalla: Part 2, The Language Model: https://openjdk.org/projects/valhalla/design-notes/state-of-valhalla/02-object-model
- State of Valhalla: Part 3, The JVM Model: https://openjdk.org/projects/valhalla/design-notes/state-of-valhalla/03-vm-model
- JVM Weekly, Project Valhalla Explained: https://www.jvm-weekly.com/p/project-valhalla-explained-how-a