← 返回信息流
AI 资讯Hacker News·4 小时前

Project Valhalla 十年磨一剑,终在 JDK 28 中落地

原标题:Project Valhalla, Explained: How a Decade of Work Arrives in JDK 28

速览

Project Valhalla 是 Java 平台一项历时十年的重大技术革新,旨在通过值类型等特性显著提升性能。该特性最终随 JDK 28 正式发布,标志着 Java 在底层架构优化上取得关键突破。此举有望解决长期存在的内存开销与性能瓶颈问题。

AI 深度解读

Project Valhalla 详解:十年磨一剑,终入 JDK 28

来源:Hacker News / JVM Weekly vol. 180 核心事件:Oracle 工程师 Lois Foltan 确认,JEP 401(值类与对象)将集成到 OpenJDK 主仓库,并计划作为预览特性出现在 JDK 28 中。


背景

Java 语言自诞生以来,其内存模型存在一个根本性的二元对立:除了 intlongdoubleboolean 等八种基本数据类型(primitives)外,所有其他类型均为引用类型(reference types)。

这种设计在早期并非问题,但随着硬件架构的演进,它成为了性能瓶颈。在 1995 年,内存访问速度与 CPU 操作速度大致相当。然而如今,CPU 的速度比主内存快两个数量级,两者之间的差距完全依靠 CPU 缓存(cache)来弥补。处理器以“缓存行”(cache line,通常为 64 字节)为单位读取内存。

当对象在堆上分散存储时,访问它们需要遵循指针(pointer indirection),这极易导致缓存未命中(cache miss),其速度可能比命中缓存慢上百倍。这就是所谓的“引用类型膨胀”(fluffy memory layout)问题:每个对象都带有对象头(metadata,约十几字节),且内存地址不连续。

为了解决这一问题,JVM 引入了逃逸分析(escape analysis)。如果 JIT 编译器能确定某个对象不会“逃逸”出局部作用域,它就不会在堆上分配该对象,而是将其字段展开为局部变量或寄存器变量,从而消除分配和垃圾回收的开销。然而,这种优化是不可预测且脆弱的。一旦对象被存入数组、传递给复杂方法或超出 JIT 分析范围,优化就会失效,导致性能剧烈波动。

因此,高性能场景(如游戏引擎、图形库、数据库、HPC 代码)往往被迫放弃面向对象的安全性,转而手动编码数据(例如用三个字节表示 RGB 颜色),但这牺牲了代码的可读性、安全性和类型检查。

Project Valhalla 的目标正是消除这种二元对立:实现“代码像类一样编写,运行像 int 一样高效”。

核心内容

1. 项目起源与演进

Project Valhalla 正式始于 2014 年。Java 之父 James Gosling 曾形容该项目为“六个博士系的难题拧成一股绳”。事实上,Java 创建者在 1995 年就已尝试引入值类型,但因技术难度过大而放弃。

在过去十年中,团队构建了五个不同的原型,探索不同的技术路径:

  • Q World 原型:早期方案假设值类型是与对象截然不同的生物,拥有独立的类型描述符、字节码和顶层类型(top types),类似于基本类型。这种分离导致 JVM 类型系统极度复杂,所有操作都需要双版本实现。
  • L World 原型(2019年左右):这是关键转折点。值类型开始与对象共享相同的“L 载体”(L descriptor,即 JVM 用于普通引用的描述符)。这一思路简化了类型系统,为最终方案奠定了基础。

2. JEP 401 的现状

2024 年 6 月 15 日,Oracle 工程师 Lois Foltan 确认 JEP 401: Value Classes and Objects 将集成到 OpenJDK 主仓库,目标版本为 JDK 28

  • 规模巨大:该变更涉及 1,816 个文件,新增代码超过 19.7 万行。由于改动巨大,贡献者被要求在集成期间暂停大型提交。
  • 预览特性:该功能默认禁用,且仅为 Valhalla 项目的第一部分。
  • 社区反应:Brian Goetz 幽默地指出,批评者将从“他们永远不会发布”转变为“他们没发布最重要的部分”。社区中流传着一个玩笑:我们可能先走进北欧神话中的英灵殿(Valhalla),项目才会完全完工。

3. 技术目标:值类(Value Classes)

JEP 401 旨在允许开发者定义“值类”,这些类在语义上表现为类(拥有方法、构造函数验证、合理的字段名),但在内存布局上表现为扁平、密集的原始数据(dense layout)。

  • 内存布局:值类的实例不存储在堆上的独立对象中,而是直接嵌入在包含它们的结构(如数组或其他对象)中。
  • 性能优势:消除了指针间接寻址,提高了数据局部性(locality of reference),从而大幅提升缓存命中率。
  • 安全性与可读性:保留了类的封装性、类型安全和代码可读性,避免了手动编码原始数据带来的错误风险(如 RGB/BGR 混淆)。

关键要点

  • JDK 28 预览:JEP 401 将在 JDK 28 中以预览特性形式出现,默认禁用,需显式启用。
  • 非最终形态:这是 Valhalla 项目的“第一部分”,后续还将引入更多优化和功能(如值类型接口、值类型数组等)。
  • 解决缓存局部性问题:核心价值在于通过扁平化内存布局,解决现代 CPU 缓存机制下的性能瓶颈,而非单纯减少对象头大小(后者由 Project Lilliput 处理)。
  • 逃逸分析的局限性:虽然逃逸分析能优化部分场景,但其效果依赖于 JIT 编译器的分析能力,具有不可预测性。Valhalla 提供的是确定性的高性能。
  • 类型系统简化:通过 L World 原型确立的“共享载体”策略,避免了早期 Q World 方案带来的类型系统分裂和复杂性爆炸。
  • 行业影响:对于数据密集型应用(科学计算、图形处理、高频交易等),Valhalla 有望在不牺牲代码质量的前提下,提供接近原生 C/C++ 的性能。

意义与影响

Project Valhalla 的集成是 Java 语言发展史上的一个里程碑。它标志着 Java 从“纯粹的对象导向”向“混合类型系统”的实质性转变。

  1. 性能范式转移:Java 将不再仅仅依赖 JIT 编译器的启发式优化,而是通过语言层面的支持,提供确定性的内存布局控制。这使得 Java 在高性能计算领域更具竞争力。
  2. 代码质量与性能的平衡:开发者无需再在“易读的类”和“高效的原始数据”之间做妥协。可以编写结构清晰、类型安全的代码,同时获得接近基本数据类型的性能。
  3. 生态系统适配:随着 JDK 28 的发布,现有的库和框架需要逐步适配值类型。这将推动 Java 生态系统的现代化,但也可能带来短期的兼容性和学习曲线挑战。
  4. 长期愿景:Valhalla 只是第一步。未来,Java 可能会进一步支持值类型的接口、泛型优化等,最终实现“代码像类,运行像 int”的终极目标。

尽管社区对“何时能完全发布”仍有调侃,但 JEP 401 的集成证明了 Java 团队对这一长期难题的持续投入和技术突破。对于关注 Java 性能极限的开发者而言,JDK 28 将是一个值得关注的版本。

查看原文 →jvm-weekly.com