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; 
}

其内存布局大致如下:

point class memory

3.1. 普通类中指针间接访问的性能问题

下图展示了在数组中使用普通 Point 类时,指针间接访问所带来的性能损耗。

每个数组槽位都持有一个指向堆中 Point 对象的引用,这不仅消耗内存,访问时还必须进行间接寻址,带来额外开销。对于大数组而言,这种访问方式会导致性能下降。此外,这些不再使用的对象仍可能驻留在内存中,引发潜在的内存泄漏。

java point vaue type array

为了解决这些问题,Project Valhalla 引入了值类(Value Class)。

4. 值类(Value Class)

值类依然是引用类型,存储在堆中,并且隐式为 final,其字段也是不可变的。

与普通类不同的是,值类通过其字段值直接编码,减少了对象头、堆分配和间接访问的开销

这使得 JVM 能够将值类型“展平”到数组、对象甚至其他值类型中。

值类的设计目标是表示纯粹的数据聚合体,因此它放弃了普通对象的标识特性。这意味着:

  • ✅ 没有对象标识(identity)
  • ✅ 相等性只能基于状态判断
  • ❌ 不支持多态、不可变引用、非空引用等依赖对象标识的特性

值类 Point 的定义和内存布局如下:

value class Point {
  int x;
  int y;
}

point class memory

值类的内存布局与普通类类似,但优化了嵌套结构,使得数组和内存存储更高效。此外,值类的修改也更高效,因为每次修改都会创建新实例,而不是修改原实例。

不过,值类仍然存在一些限制,例如:

  • ⚠️ 值类型变量仍可能为 null,需要额外的位来表示
  • ⚠️ 作为堆引用,值对象必须原子修改,不利于内联优化

为了解决这些问题,Project Valhalla 提出了原始类(Primitive Classes)。

5. 原始类(Primitive Classes)

原始类相比值类,提供了更强的性能优势。它们的行为类似于基本类型,存储在栈中,每个原始类型实例属于一个线程,多个线程不能共享同一个原始实例,因此无需原子操作,避免了性能损耗。

原始类的实例本质上是基本类型的组合,并可以附加实用方法。

下面是原始类型数组 Point[] 的示意图:

java point vaue type array values

原始类使得 JVM 可以在栈上直接传递值类型,而无需分配到堆中。最终,我们可以得到行为类似于 intfloat 等基本类型的数据聚合体

与基本类型不同的是,原始类可以:

  • ✅ 拥有方法和字段
  • ✅ 实现接口
  • ✅ 作为泛型类型使用

原始类为我们提供了两种视角:

  • ✅ 更快的对象(Faster Objects)
  • ✅ 用户定义的基本类型(User-defined Primitives)

额外的好处是,原始类可以作为泛型类型使用,无需装箱。这也引出了 Valhalla 的另一个重要特性:特化泛型(Specialized Generics),我们将在后面介绍。

6. 基本类型的类封装(Wrapper Classes)

从 Java 5 开始,基本类型可以通过包装类(如 IntegerDouble)以对象形式使用,支持自动装箱/拆箱。但这带来了运行时开销,例如:

  • ⚠️ 装箱操作消耗性能
  • ⚠️ 相同值的装箱对象可能不相等(== 失效)

Project Valhalla 计划将现有的包装类(如 java.lang.Integer)迁移到原始类,从而消除将基本值建模为对象的大部分开销。这样,基本类型就能以类的形式使用,同时获得类的所有能力,而无需牺牲性能。

7. 特化泛型(Enhanced Generics)

目前在 Java 中使用泛型时,基本类型必须使用包装类(如 Integer 对应 int),这引入了额外的间接访问,反而抵消了基本类型带来的性能优势

因此,许多框架和库都提供了专门的基本类型泛型版本,如 IntStreamToIntFunction 等,用于避免性能损耗。

Project Valhalla 的目标是通过特化泛型,让泛型支持所有类型:

  • ✅ 对象引用
  • ✅ 基本类型
  • ✅ 值类型
  • ✅ 甚至 void

从而彻底摆脱“手动优化”的 hack 做法。

8. 总结

Project Valhalla 将为 Java 带来两个核心改进:

  • 性能提升:通过展平对象图、减少间接访问,优化内存布局,减少分配和 GC 压力
  • 更好的抽象一致性:原始类型、值类型和引用类型在泛型中行为更加统一

目前,Valhalla 的早期原型 LW1 已经将值类型引入现有类型系统。

更多关于 Project Valhalla 的信息,可以参考以下链接:


原始标题:Java Valhalla Project