Java Project Valhalla个人研究与尝鲜

65 阅读5分钟

要说Java有什么我特别期待但还没有正式上线的新特性,那就必然是Project Valhalla了。

这个项目已经开发了超过10年,外网有人评论说:I hope I can use Java with Valhalla before getting to Valhalla.

Project Valhalla是什么

Java Project Valhalla 是 OpenJDK 社区主导的一项长期项目,旨在通过引入 值类型(Value Types)专用泛型(Specialized Generics) 等特性,优化 Java 的内存模型和性能,使其更好地适应现代硬件架构。

实际上就是实现Java的值类型,类似于C/C++中的struct。 Java 的值类型,去掉了对象头,只存储它其中的值。这样可以很大程度上的省下对象占用的空间,并且可以直接实现在栈上分配,提高效率。但是没有对象头也使得它无法使用sychronized进行同步,以及一些其他需要对象头的操作。

Project Valhalla的目标

在JVM中有两种值类型,一种是基本类型(primitive values),一种是对象引用类型(object references)。对于复杂的对象引用,每一个field本质上都是一个指针,指向另一个对象。

pEWaaO1.png

这样就会导致几个问题

  • 内存寻址很频繁,每次都需要指针进行访问,内存并不连续,访问会跳来跳去。对于现代CPU的缓存并不友好。

       也因此Java被认为并不适合用作科学计算,不适合用于大规模计算密集型任务。

  • 每一个对象都有一个对象头,实际上我们拿对象头并没有用,但是还是会占用空间,造成空间的浪费。

    Project Valhalla的目标就是在Java中实现类似C/C++中的struct的内存布局

pEWah0P.png

Java的值类型和引用类型的割裂,使得用户必须在 为了高性能而使用原始类型写出不可维护的代码,以及为了可读性而使用类进行封装,放弃高性能之间做取舍。

还有对于泛型,在Java中有一个老生常谈的问题,就是泛型擦除,泛型在运行时会变成Object,类型信息会被擦除,以及Java Integer int基本类型装箱拆箱,Integer -128~127的缓存导致的 == 的坑,等等,几乎在每个Java八股文面试题中都会问到。

对于ArrayList,不管你写的是什么类型,在ArrayList内部其实就是一个Object[],只是在编译器添加了强制转换。因此对于两个函数,如果参数一个是ArrayList<Integer>,一个是ArrayList<Long>,泛型擦除之后都是ArrayList<Object>,所以这两个函数无法进行重载。

Project Valhalla也是为了解决这些问题而存在,让我们可以实现直接写ArrayList<int>以及ArrayList<long>,并且内部的数组就是int[]long[]

因此,Project Valhalla的目标就是实现对Java值类型和引用类型的统一,最后理想情况下就是Codes like a class, Works like an int

Project Valhalla的一些进展

目前jdk已经推出JEP 401(Value Classes),也就是一个value关键字,可以放在class上,代表他是一个值类型。

然后将jdk中一些类向值类型迁移。

比如Integer, Long,等等,参考Java 11官方文档给出的Value-based Classes的定义

  • 是final和不可变的(尽管可能包含对可变对象的引用);
  • 具有 equals、hashCode 和 toString 的实现,这些实现仅根据实例的状态计算,而不是根据其identity或任何其他对象或变量的状态计算;(也就是说只基于对象内部的值,而不依赖于自身位于内存的哪个地址)
  • 不使用identity-sensitive操作,例如实例之间的引用相等性(==)、实例的身份哈希码或实例的固有锁上的同步;
  • 仅基于 equals() 被认为是相等的,而不是基于引用相等 (==);
  • 没有可访问的构造函数,而是通过工厂方法实例化,而工厂方法对返回实例的内存地址不做任何保证;
  • 当相等时可以自由替换,这意味着在任何计算或方法调用中交换根据 equals() 相等的任何两个实例 x 和 y 都不会产生明显的行为变化。(也就是说两个equal的对象,行为应该完全相同)

关于更多的资讯,B站也有很多的视频,[Java-Project Valhalla]Project Valhalla 2024 年末的进展_哔哩哔哩_bilibili,大家感兴趣也可以自行了解。

关于Project Valhalla与JDK 16推出的Record的区别

record其实和本质上的class没有区别,只是继承了abstract class java.lang.Record,所有的字段都不允许修改。并且不需要写Getter和Setter,Java会自动为你生成getter,equals,hashcode,toString函数,基本可以算的上是lombok的官方替代品。

Record并没有实现Project Valhallainline Object,仍然可以使用record对象进行synchronized以及Object.wait()以及Object.notify()方法。Record 本质是语法糖,通过编译时生成代码实现,底层仍为普通类对象,具备对象头和堆分配特性

特性Record 类Valhalla 值类型
对象头保留对象头,支持同步操作(synchronized无对象头,不支持同步操作
分配位置堆分配(普通对象)栈或父对象内存中直接分配
内存连续性数组元素通过指针引用,不连续存储数组元素连续存储,无指针开销
身份敏感操作支持(如 ==System.identityHashCode()不支持,基于值而非身份标识
可变性组件(字段)为 final,但可引用可变对象完全不可变(需开发者保证)

Record 类是 Valhalla 的铺垫,Record 的不可变特性与 Valhalla 的设计理念部分契合,但未触及内存模型优化。未来 Valhalla 值类型可能扩展 Record 的语义,使其成为值类型的语法表现形式。