想象一下,你正站在一座古老的城堡前。这座城堡名叫“Java”,它已经屹立三十年,墙壁厚实、房间无数,却始终藏着一个致命的裂缝——每间屋子都必须挂一块沉重的门牌(对象头),每条走廊都铺满指向屋子的路标(引用)。有一天,一位老工匠Dan Smith手持一封盖着红蜡的信,宣布:“我们要拆掉门牌,把走廊直接铺成地板。”这一刻,整个城堡都颤抖了。
这就是2026年初HotSpot团队发出的那封“预警”邮件带来的震动:Project Valhalla的核心——Value Classes(值类)——即将合并进JDK主线。下面,让我们一起走进这场迟到了十多年的底层革命,看看它到底会把Java带向何方。
2026年1月,OpenJDK邮件列表上出现了一封标题平平无奇、内容却石破天惊的邮件。发件人正是Dan Smith——那位当年一刀雕出Lambda表达式的语言规范守护者。他用平静的语气写道:
“This will be a very large commit touching many components of HotSpot... start aligning your ports with the anticipated changes.”翻译过来就是:“我们即将提交一个超级大的变更,会改动HotSpot的很多部件,请各位端口维护者提前准备。”
/ipfs/QmNWHBTDkoSUADttnStTLUxiNLuKMJX4yqmxXTYVZvk4y7?filename=v2-9f39884a9aab0b0a24d3cd98291570e9r.jpg
这封邮件透露了三个关键信号:
acmp(即==比较)、monitorenter(监视器进入,即锁)等底层行为都会变化。ACC_IDENTITY严格区分“有身份的对象”和“无身份的值对象”。int、double一样轻量、扁平、可直接内嵌。
要理解这场变革的必要性,我们得回到Java最根本的设计抉择:万物皆对象。
在1995年,这是一个大胆而优雅的想法:把一切都包装成对象,带来统一的内存管理、安全性和可移植性。可三十年后,它也成了性能的枷锁。
举一个最经典的例子:一个二维点坐标。
class Point {
final int x;
final int y;
}
当你创建一个Point[]数组时,内存里长什么样?

左边是传统引用对象布局:数组里存的是一堆指针,每个指针又指向堆上一个带着12~16字节对象头的Point实例。两个int才8字节,却要额外背负对象头、对齐填充、指针开销——总开销可能比数据本身还大。
右边是即将到来的值类布局:数组里直接是[x,y][x,y][x,y]...,连续、紧凑、没有对象头、没有指针跳跃。
这带来的后果是毁灭性的:
PriceLevel对象(价格+数量)。迁移到C++后,仅仅因为内存布局更紧凑,同等硬件下延迟从微秒级降到亚微秒级,内存占用砍掉近60%。Java工程师们只能眼睁睁看着Rust社区嘲笑“Java是贵族语言——贵在内存贵”。
JEP 401引入的语法非常克制,只多了一个value关键字:
value class Point {
int x;
int y;
Point translate(int dx, int dy) {
return new Point(x + dx, y + dy);
}
}
看似只加了一个词,背后却是JVM对象模型的颠覆:
==比较两个Point是否“是同一个对象”(因为它们根本没有对象身份),也不能对它们synchronized加锁。它们的行为更像int:a == b比较的是内容是否相等。
final,实例创建后不可修改(虽然未来可能有mutable value class的变种,但当前预览版是immutable)。
想象一下:一个包含数百万Point的Polygon对象,传统方式需要数百万个独立对象头和指针;值类方式下,可能整个多边形的所有顶点数据都连续地躺在一段内存里,就像C语言的struct Point points[1000000]那样。
邮件里反复提到的ACC_IDENTITY标志,是这场变革的分水岭。
ACC_IDENTITY的类:传统对象,可用==比较地址、可加锁、可有可变状态。换句话说,JVM终于承认:并不是所有数据都需要“身份”。数字、坐标、颜色、货币金额、矩阵元素……这些本质上是“值”的东西,不该为了一刀切的哲学付出性能代价。
java.time系列、Optional、记录类record的候选者)。长期看,新的API设计范式会涌现:更多不可变、扁平、零开销抽象。
三十年前,James Gosling设计Java时,可能没想到对象头会成为日后最大的性能瓶颈。今天,Dan Smith和Valhalla团队用一场迟到的手术,为这门语言注入了新的生命力。
当值类真正落地那天,我们或许会看到:
如果你也曾为new Point(1,2)背后的12字节对象头而叹气,为缓存未命中而挠头,那么请准备好迎接一个完全不同的Java时代吧。
值对象的时代,即将开启。
参考文献
还没有人回复