从openJDK几个项目看java未来发展

前言

java 发展了几十年,一直是编程语言当中的主流语言。这与其本身的很多优秀特质是分不开的,例如:

  • 强大的 JVM: 让用户可以专注业务,让虚拟机自动化地管理内存。相比 C、C++,这大大提升了工程效率
  • 丰富的生态:作为一款发展 25 年以上的编程语言,使用其开发的软件不计其数,无论做什么工作,都能找到你需要的工具软件、三方包、学习资料等。

毫无疑问,java 肯定是一门优秀的编程语言,但仍然有不足。很多后起之秀的编程语言也开始对 java 发起挑战。本文主要以 java 官方推进的项目展开来看看 java 当下的不足,以及未来发展的趋势。

java 官方项目

这个可以在 openJDK 的官网上查阅到具体的 project 详情,可以点我查看。下图是其中关注度比较高的一些项目,本文接单做一些介绍。

Panama

github 上可以查看项目。这个项目主要就是在 jdk 层面提供对别的语言更加高效方便的访问。可以做的事情诸如:

  • 如你可以在 java 中通过 jextract 自动将 c 的 header 文件改写成 java 接口和方法
  • 在 java 中更加方便的访问 c 的链接库,例如下图的 unistd

  • 调用其他语言的方法。参考下 JNI 做的事情,panama 使得这种调用更加容易,这样可以利用 c 和 c++的库做一些方便的 native 调用

Valhalla

Java 一切皆对象的理念对于工程化是很有帮助的,但是处理一些列类型不同的小对象时性能十分拉胯。Valhalla 项目就是通过引入值对象来改善这一问题,使得 java 在游戏、图形领域也能有强的竞争力。

JIT 从 JDK6 开始就通过逃逸分析来做做标量替换(Scalar Replacement)和栈上分配(Stack Allocations)来优化性能。即在对象不会传递到方法外的时候不创建完整对象布局,通过更加紧凑、平摊的方式分配内存提升 CPU 处理的效率。这里可以横向对比下 c 和 c++中的 struct,其实本质是值对象,不会有 java 开销较高的类对象内存布局。

原生的值类型带来如下好处:

  • 节约内存、性能更好:更加紧凑的内存分配,很多时候可以直接做 cache line 优化,避免频繁访问 ram,陷入冯诺依曼瓶颈
  • 泛型支持原生类型
  • 统一 Integer 和 int,在语言层面避免拆装箱

了解更加详细的 valhalla 背景可以看下下面文章:State of Valhalla

Loom

该项目旨在拓展 jdk 1:1 的线程模型,引入协程(coroutine)。以前 jdk 的线程模型和操作系统线程 1:1 对应,适合 CPU 密集型任务,但是对于需要大量线程的 I/O 密集型任务,过重的线程开销和频繁的上下文切换对性能开销都比较大。

除了引入协程,还有个重点是支持结构化并发的编程范式(2016 年 ZerMQ 作者提出),这将大大简化并发编程使用的门槛,减少出错概率。简而言之这种结构化编程理念即“Code like sync,Work like async”。下面给出一个例子:

1
2
3
4
5
6
// 通过花括号的结构来封装协程,代码块异步执行结束会自动等待
ThreadFactory factory = Thread.builder().virtual().factory();
try (var executor = Executors.newThreadExecutor(factory)) {
executor.submit(task1);
executor.submit(task2);
} // blocks and waits

Structured Concurrency 核心在于通过一种 structured 的方法实现并发程序,用具有明确入口点和出口点的控制流结构来封装并发“线程”(可以是系统级线程也可以是用户级线程,也就是协程,甚至可以是进程)的执行,确保所有派生“线程”在出口之前完成。

Amber

amber 项目主要是 java 语言本身层面的一些特性支持,参考他关联的一些 JEP,例如 record,var 等。

Leyden

这个项目成立是为了解决java 启动慢的问题。用 java 写过大型项目的同学都有体会,java 重启的过程还是比较重的,其中主要开销是类的加载连接初始化。java 是解释+JIT 一起工作的,leyden 项目的目的就是替换老的这种方式,称之为 static-image,相比传统 AOT 只是直接把代码编译成机器码,static-image 除了把代码编译成机器码,还会把 runtime 信息一起编译成机器码,在运行时能够完整运行。

类似使用 static-image 这样技术的项目还有GraalVM

思考:为什么云原生 golang 的应用更具优势?

  • 云原生技术本质是操作系统虚拟化,golang 相比更加 native,对操作系统的调用更加直接
  • 微服务、大量容器,golang 内存更有优势:java 解释器+JIT 的模式内存开销更多。

采用类 AOT 的提前编译负面影响

  • 提前编译影响类的动态加载
  • 反射无法工作:反射原本可以调用编译期不可知的方法,现在也无法做到
  • 动态代理
  • 字节码生成
  • 元编程能力弱,复杂工程代码可能会越来越臃肿

这些影响,都需要对应的项目去解决

tips: 从当前发展来看,oracle lab 独立维护的 graalVM 可能是这个赛道上的胜出者

Portola

主要是 jdk 对 Alpine Linux 的支持。Alpine Linux 基础镜像 5MB,是很多容器首选镜像。Alpine Linux 不使用 GUN C 还是用 musl 作为 C 标准库,JDK 主要是适配这块。

Portola 项目使得 jdk 能够被更好地应用于容器环境。

总结

总的来说 java 仍然是一门非常有潜力的原因,尽管他有这样那样的不好,但是他也一直在变得更好。在深入了解过各个 jdk projects 以后,后续看不同 JDK 的发布以及他们关联的 JEP 时,心中会更加的清晰。

参考资料