1. 概述
在本文中,我们将深入探讨 Project Valhalla —— 这一 Java 平台历史上具有深远影响的项目。我们会回顾其诞生的历史背景、当前的发展状态,以及它将为日常 Java 开发者带来哪些实质性的改进。
2. Valhalla 项目的动机与原因
Oracle 的 Java 语言架构师 Brian Goetz 在一次演讲中提到,Valhalla 项目的主要动机之一是 让 Java 语言和运行时适应现代硬件架构。
回想 Java 语言诞生之初(大约在 25 年前),内存访问和算术运算的成本几乎相同。然而现在,内存访问的开销比算术运算高出 200 到 1000 倍。从语言设计角度来看,这意味着 间接访问(indirection)带来的指针获取会显著影响性能。
由于大多数 Java 数据结构都是对象,我们可以认为 Java 是一种“指针密集型”语言(虽然我们通常不直接操作指针)。对象的这种指针式实现是为了支持对象标识(identity),而对象标识又支撑了语言中的多态、可变性、锁等特性。但问题是,这些特性对每个对象都默认提供,无论是否真的需要。
顺着“对象标识 → 指针 → 间接访问 → 性能损耗”的链条,一个自然的结论是:对于不需要这些特性的数据结构,应该移除这些不必要的间接访问。这就是值类型(Value Types)的由来。不过在此之前,我们先来看看普通的类。
3. 普通类(Regular Class)
目前在 Java 中,普通类的定义如下:
class Point {
int x;
int y;
}
其内存布局大致如下:
3.1. 普通类中指针间接访问的性能问题
下图展示了在数组中使用普通 Point
类时,指针间接访问所带来的性能损耗。
每个数组槽位都持有一个指向堆中 Point
对象的引用,这不仅消耗内存,访问时还必须进行间接寻址,带来额外开销。对于大数组而言,这种访问方式会导致性能下降。此外,这些不再使用的对象仍可能驻留在内存中,引发潜在的内存泄漏。
为了解决这些问题,Project Valhalla 引入了值类(Value Class)。
4. 值类(Value Class)
值类依然是引用类型,存储在堆中,并且隐式为 final
,其字段也是不可变的。
与普通类不同的是,值类通过其字段值直接编码,减少了对象头、堆分配和间接访问的开销。
这使得 JVM 能够将值类型“展平”到数组、对象甚至其他值类型中。
值类的设计目标是表示纯粹的数据聚合体,因此它放弃了普通对象的标识特性。这意味着:
- ✅ 没有对象标识(identity)
- ✅ 相等性只能基于状态判断
- ❌ 不支持多态、不可变引用、非空引用等依赖对象标识的特性
值类 Point
的定义和内存布局如下:
value class Point {
int x;
int y;
}
值类的内存布局与普通类类似,但优化了嵌套结构,使得数组和内存存储更高效。此外,值类的修改也更高效,因为每次修改都会创建新实例,而不是修改原实例。
不过,值类仍然存在一些限制,例如:
- ⚠️ 值类型变量仍可能为
null
,需要额外的位来表示 - ⚠️ 作为堆引用,值对象必须原子修改,不利于内联优化
为了解决这些问题,Project Valhalla 提出了原始类(Primitive Classes)。
5. 原始类(Primitive Classes)
原始类相比值类,提供了更强的性能优势。它们的行为类似于基本类型,存储在栈中,每个原始类型实例属于一个线程,多个线程不能共享同一个原始实例,因此无需原子操作,避免了性能损耗。
原始类的实例本质上是基本类型的组合,并可以附加实用方法。
下面是原始类型数组 Point[]
的示意图:
原始类使得 JVM 可以在栈上直接传递值类型,而无需分配到堆中。最终,我们可以得到行为类似于 int
、float
等基本类型的数据聚合体。
与基本类型不同的是,原始类可以:
- ✅ 拥有方法和字段
- ✅ 实现接口
- ✅ 作为泛型类型使用
原始类为我们提供了两种视角:
- ✅ 更快的对象(Faster Objects)
- ✅ 用户定义的基本类型(User-defined Primitives)
额外的好处是,原始类可以作为泛型类型使用,无需装箱。这也引出了 Valhalla 的另一个重要特性:特化泛型(Specialized Generics),我们将在后面介绍。
6. 基本类型的类封装(Wrapper Classes)
从 Java 5 开始,基本类型可以通过包装类(如 Integer
、Double
)以对象形式使用,支持自动装箱/拆箱。但这带来了运行时开销,例如:
- ⚠️ 装箱操作消耗性能
- ⚠️ 相同值的装箱对象可能不相等(
==
失效)
Project Valhalla 计划将现有的包装类(如 java.lang.Integer
)迁移到原始类,从而消除将基本值建模为对象的大部分开销。这样,基本类型就能以类的形式使用,同时获得类的所有能力,而无需牺牲性能。
7. 特化泛型(Enhanced Generics)
目前在 Java 中使用泛型时,基本类型必须使用包装类(如 Integer
对应 int
),这引入了额外的间接访问,反而抵消了基本类型带来的性能优势。
因此,许多框架和库都提供了专门的基本类型泛型版本,如 IntStream
、ToIntFunction
等,用于避免性能损耗。
Project Valhalla 的目标是通过特化泛型,让泛型支持所有类型:
- ✅ 对象引用
- ✅ 基本类型
- ✅ 值类型
- ✅ 甚至
void
从而彻底摆脱“手动优化”的 hack 做法。
8. 总结
Project Valhalla 将为 Java 带来两个核心改进:
- ✅ 性能提升:通过展平对象图、减少间接访问,优化内存布局,减少分配和 GC 压力
- ✅ 更好的抽象一致性:原始类型、值类型和引用类型在泛型中行为更加统一
目前,Valhalla 的早期原型 LW1 已经将值类型引入现有类型系统。
更多关于 Project Valhalla 的信息,可以参考以下链接: