云原生时代的Java

332 阅读25分钟

1. GraalVM是什么

image.png

1.1 前言

网上每隔一段时间就能见到几条“未来 X 语言将会取代 Java”的新闻,此处“X”可以用 Kotlin、Golang、Dart、JavaScript、Python等等各种编程语言来代入。这大概就是长期占据编程语言榜单第一位的烦恼,天下第一总避免不了挑战者相伴。如果 Java 有拟人化的思维,它应该从来没有惧怕过被哪一门语言所取代,Java“天下第一”的底气不在于语法多么先进好用,而是来自它庞大的用户群和极其成熟的软件生态,这在朝夕之间难以撼动。不过,既然有那么多新、旧编程语言的兴起躁动,说明必然有其需求动力所在,譬如互联网之于JavaScript、人工智能之于 Python,微服务风潮之于 Golang 等等。大家都清楚不太可能有哪门语言能在每一个领域都尽占优势,Java 已是距离这个目标最接近的选项,但若“天下第一”还要百尺竿头更进一步的话,似乎就只能忘掉 Java 语言本身,踏入无招胜有招的境界。

1.2 简介

2018 年 4 月,Oracle Labs 新公开了一项黑科技:Graal VM,从它的口号“ Run Programs Faster Anywhere” 就能感觉到一颗蓬勃的野心,这句话显然是与 1995 年 Java 刚诞生时的“Write Once,Run Anywhere”在遥相呼应。

image.png

就是说可以让java、scala、ruby、python、r等语言能够在任何地方跑的更快。特别是目前云原生的条件下,能够使得java运行的速度更快(启动速度更快,运行速度更快) Graal VM 被官方称为“Universal VM”和“Polyglot VM”,这是一个在 HotSpot 虚拟机基础上增强而成的跨语言全栈虚拟机,可以作为“任何语言”的运行平台使用,这里“任何语言”包括了 Java、Scala、Groovy、Kotlin 等基于 Java 虚拟机之上的语言,还包括了 C、C++、Rust 等基于 LLVM 的语言,同时支持其他像 JavaScript、Ruby、Python 和 R 语言等等。Graal VM 可以无额外开销地混合使用这些编程语言,支持不同语言中混用对方的接口和对象,也能够支持这些语言使用已经编写好的本地库文件。

1.3 基本工作原理

Graal VM 的基本工作原理是将这些语言的源代码(例如 JavaScript)或源代码编译后的中间格式(例如 LLVM 字节码)通过解释器转换为能被 Graal VM 接受的中间表示(Intermediate Representation,IR),譬如设计一个解释器专门对 LLVM 输出的字节码进行转换来支持 C 和 C++语言,这个过程称为“程序特化”(Specialized,也常称为 Partial Evaluation)。Graal VM 提供了Truffle 工具集来快速构建面向一种新语言的解释器,并用它构建了一个称为Sulong的高性能 LLVM 字节码解释器。

LLVM

:::info LLVM是一组模块化和可重用的编译器和工具链技术,旨在提供高质量的代码生成和优化,支持多种编程语言。LLVM项目最初是由苹果公司开发的,现已成为一个开源社区项目。LLVM支持C、C++、Objective-C、Swift等编程语言,并支持多种目标平台,如X86、ARM、MIPS、PowerPC等。 ::: 以下是LLVM的一些主要组件和功能:

组件/功能描述
LLVM IRLLVM的中间表示,类似于一种低级汇编语言
Clang基于LLVM的C/C++编译器
LLD基于LLVM的链接器
LLVM MC用于汇编和反汇编的库
LLVM Object用于生成和操作目标文件的库
LLVM Bitcode一种中间表示,可用于将代码从一种平台编译到另一种平台
LLVM Passes一组优化器和转换器,用于优化代码和转换LLVM IR
LLVM Target支持多种目标平台的代码生成器

除了这些组件和功能外,LLVM还提供了丰富的工具和库,包括LLDB调试器、LLVM静态分析器、libc++库等,以及多种插件和扩展,可用于增强LLVM的功能和性能。 image.png

1.4 特性总结

其实,以更严格的角度来看,Graal VM 才是真正意义上与物理计算机相对应的高级语言虚拟机,理由是它与物理硬件的指令集一样,做到了只与机器特性相关而不与某种高级语言特性相关。Oracle Labs 的研究总监 Thomas Wuerthinger 在接受InfoQ 采访时谈到:“随着 Graal VM 1.0 的发布,我们已经证明了拥有高性能的多语言虚拟机是可能的,并且实现这个目标的最佳方式不是通过类似 Java 虚拟机和微软 CLR 那样带有语言特性的字节码”。对于一些本来就不以速度见长的语言运行环境,由于 Graal VM 本身能够对输入的中间表示进行自动优化,在运行时还能进行即时编译优化,往往使用 Graal VM 实现能够获得比原生编译器更优秀的执行效率,譬如 Graal.js 要优于 Node.js、Graal.Python 要优于 CPtyhon,TruffleRuby 要优于 Ruby MRI,FastR 要优于 R 语言等等。 针对 Java 而言,Graal VM 本来就是在 HotSpot 基础上诞生的,天生就可作为一套完整的符合 Java SE 8 标准的 Java 虚拟机来使用。它和标准的 HotSpot 差异主要在即时编译器上,其执行效率、编译质量目前与标准版的 HotSpot 相比也是互有胜负。但现在 Oracle Labs 和美国大学里面的研究院所做的最新即时编译技术的研究全部都迁移至基于 Graal VM 之上进行了,其发展潜力令人期待。如果 HotSpot 虚拟机真的有被取代的一天,那从现在看来 Graal VM 是希望最大的一个候选项,这场革命很可能会在 Java 使用者没有明显感觉的情况下悄然而来,Java 世界所有的软件生态都没有发生丝毫变化,但天下第一的位置已经悄然更迭。

2. 新一代即时编译器

2.1 Java的性能巅峰

对需要长时间运行的应用来说,由于经过充分预热,热点代码会被 HotSpot 的探测机制准确定位捕获,并将其编译为物理硬件可直接执行的机器码,在这类应用中 Java 的运行效率很大程度上是取决于即时编译器所输出的代码质量。也就是说目前的Java要达到一个性能巅峰的情况下,必须是等项目足够预热后,热点代码编译等之后是执行效率最高的一个阶段

2.2 JIT中的编译器简介

   HotSpot 虚拟机中包含有两个即时编译器,分别是编译时间较短但输出代码优化程度较低的客户端编译器(简称为 C1)以及编译耗时长但输出代码优化质量也更高的服务端编译器(简称为 C2),通常它们会在分层编译机制下与解释器互相配合来共同构成 HotSpot 虚拟机的执行子系统的。自 JDK 10 起,HotSpot 中又加入了一个全新的即时编译器:Graal 编译器,看名字就可以联想到它是来自于前一节提到的 Graal VM。Graal 编译器是作为 C2 编译器替代者的角色登场的。C2 的历史已经非常长了,可以追溯到 **Cliff Click** 大神读博士期间的作品,这个由 C++写成的编译器尽管目前依然效果拔群,但已经复杂到连** Cliff Click **本人都不愿意继续维护的程度。而 Graal 编译器本身就是由 Java 语言写成,实现时又刻意与 C2 采用了同一种名为“Sea-of-Nodes”的高级中间表示(High IR)形式,使其能够更容易借鉴 C2 的优点。Graal 编译器比 C2 编译器晚了足足二十年面世,有着极其充沛的后发优势,在保持能输出相近质量的编译代码的同时,开发效率和扩展性上都要显著优于 C2 编译器,这决定了 C2 编译器中优秀的代码优化技术可以轻易地移植到 Graal 编译器上,但是反过来 Graal 编译器中行之有效的优化在 C2 编译器里实现起来则异常艰难。这种情况下,Graal 的编译效果短短几年间迅速追平了 C2,甚至某些测试项中开始逐渐反超 C2 编译器。Graal 能够做比 C2 更加复杂的优化,如“[部分逃逸分析](http://www.ssw.uni-linz.ac.at/Research/Papers/Stadler14/Stadler2014-CGO-PEA.pdf)”(Partial Escape Analysis),也拥有比 C2 更容易使用“[激进预测性优化](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.78.6063)”(Aggressive Speculative Optimization)的策略,支持自定义的预测性假设等等。

2.3 Graal应用情况

今天的 Graal 编译器尚且年幼,还未经过足够多的实践验证,所以仍然带着“实验状态”的标签,需要用开关参数去激活,这让笔者不禁联想起 JDK 1.3 时代,HotSpot 虚拟机刚刚横空出世时的场景,同样也是需要用开关激活,也是作为 Classic 虚拟机的替代品的一段历史。 Graal 编译器未来的前途可期,作为 Java 虚拟机执行代码的最新引擎,它的持续改进,会同时为 HotSpot 与 Graal VM 注入更快更强的驱动力。 伴随着JDK17的发布,Graal编译器被移除了,可见当前的优化效果其实并不理想,但是仍然会在回来进行持续改进,希望有朝一日能够超越hotspot,到时候可能会被应用在新版本的jdk安装包中。其实本身将Graal放进HotSpot应该就是oracel的一步无奈之举。

3. 向原生迈进

3.1 Java在云原生中的现状转型思路

对不需要长时间运行的,或者小型化的应用而言,Java(而不是指 Java ME)天生就带有一些劣势,这里并不光是指跑个 HelloWorld 也需要百多兆的 JRE 之类的问题,而更重要的是指近几年从大型单体应用架构向小型微服务应用架构发展的技术潮流下,Java 表现出来的不适应。 在微服务架构的视角下,应用拆分后,单个微服务很可能就不再需要再面对数十、数百 GB 乃至 TB 的内存,有了高可用的服务集群,也无须追求单个服务要 7×24 小时不可间断地运行,它们随时可以中断和更新;但相应地,Java 的启动时间相对较长、需要预热才能达到最高性能等特点就显得相悖于这样的应用场景。在无服务架构中,矛盾则可能会更加突出,比起服务,一个函数的规模通常会更小,执行时间会更短,当前最热门的无服务运行环境 AWS Lambda 所允许的最长运行时间仅有 15 分钟。 一直把软件服务作为重点领域的 Java 自然不可能对此视而不见,在最新的几个 JDK 版本的功能清单中,已经陆续推出了跨进程的、可以面向用户程序的类型信息共享(Application Class Data Sharing,AppCDS,允许把加载解析后的类型信息缓存起来,从而提升下次启动速度,原本 CDS 只支持 Java 标准库,在 JDK 10 时的 AppCDS 开始支持用户的程序代码)、无操作的垃圾收集器(Epsilon,只做内存分配而不做回收的收集器,对于运行完就退出的应用十分合适)等改善措施。而酝酿中的一个更彻底的解决方案,是逐步开始对提前编译(Ahead of Time Compilation,AOT)提供支持。 提前编译是相对于即时编译的概念,提前编译能带来的最大好处是 Java 虚拟机加载这些已经预编译成二进制的库之后就能够直接调用,而无须再等待即时编译器在运行时将其编译成二进制机器码。理论上,提前编译可以减少即时编译带来的预热时间,减少 Java 应用长期给人带来的“第一次运行慢”不良体验,可以放心地进行很多全程序的分析行为,可以使用时间压力更大的优化措施。 但是提前编译的坏处也很明显,它破坏了 Java“一次编写,到处运行”的承诺,必须为每个不同的硬件、操作系统去编译对应的发行包。也显著降低了 Java 链接过程的动态性,必须要求加载的代码在编译期就是全部已知的,而不能再是运行期才确定,否则就只能舍弃掉已经提前编译好的版本,退回到原来的即时编译执行状态。 早在 JDK 9 时期,Java 就提供了实验性的 Jaotc 命令来进行提前编译,不过多数人试用过后都颇感失望,大家原本期望的是类似于 Excelsior JET 那样的编译过后能生成本地代码完全脱离 Java 虚拟机运行的解决方案,但 Jaotc 其实仅仅是代替掉即时编译的一部分作用而已,仍需要运行于 HotSpot 之上。

3.2 Substrate VM闪亮登场

Substrate VM出现,才算是满足了人们心中对 Java 提前编译的全部期待。Substrate VM 是在 Graal VM 0.20 版本里新出现的一个极小型的运行时环境,包括了独立的异常处理、同步调度、线程管理、内存管理(垃圾收集)和 JNI 访问等组件,目标是代替 HotSpot 用来支持提前编译后的程序执行。它还包含了一个本地镜像的构造器(Native Image Generator)用于为用户程序建立基于 Substrate VM 的本地运行时镜像。这个构造器采用指针分析(Points-To Analysis)技术,从用户提供的程序入口出发,搜索所有可达的代码。在搜索的同时,它还将执行初始化代码,并在最终生成可执行文件时,将已初始化的堆保存至一个堆快照之中。这样一来,Substrate VM 就可以直接从目标程序开始运行,而无须重复进行 Java 虚拟机的初始化过程。但相应地,原理上也决定了 Substrate VM 必须要求目标程序是完全封闭的,即不能动态加载其他编译期不可知的代码和类库。基于这个假设,Substrate VM 才能探索整个编译空间,并通过静态分析推算出所有虚方法调用的目标方法。 Substrate VM 带来的好处是能显著降低内存占用及启动时间,由于 HotSpot 本身就会有一定的内存消耗(通常约几十 MB),这对最低也从几 GB 内存起步的大型单体应用来说并不算什么,但在微服务下就是一笔不可忽视的成本。根据 Oracle 官方给出的测试数据,运行在 Substrate VM 上的小规模应用,其内存占用和启动时间与运行在 HotSpot 相比有了 5 倍到 50 倍的下降,具体结果如下图所示: :::info Helidon Micronaut Quarkus 是一套微服务性能测试框架,如果有需要了解的小伙伴,可以自行搜索了解下呀 ::: image.png image.png Substrate VM 补全了 Graal VM“Run Programs Faster Anywhere”愿景蓝图里最后的一块拼图,让 Graal VM 支持其他语言时不会有重量级的运行负担。譬如运行 JavaScript 代码,Node.js 的 V8 引擎执行效率非常高,但即使是最简单的 HelloWorld,它也要使用约 20MB 的内存,而运行在 Substrate VM 上的 Graal.js,跑一个 HelloWorld 则只需要 4.2MB 内存而已,且运行速度与 V8 持平。Substrate VM 的轻量特性,使得它十分适合于嵌入至其他系统之中,譬如Oracle 自家的数据库就已经开始使用这种方式支持用不同的语言代替 PL/SQL 来编写存储过程。

4. 没有虚拟机的 Java

尽管 Java 已经看清楚了在微服务时代的前进目标,但是,Java 语言和生态在微服务、微应用环境中的天生的劣势并不会一蹴而就地被解决,通往这个目标的道路注定会充满荆棘;尽管已经有了放弃“一次编写,到处运行”、放弃语言动态性的思想准备,但是,这些特性并不单纯是宣传口号,它们在 Java 语言诞生之初就被植入到基因之中,当 Graal VM 试图打破这些规则的同时,也受到了 Java 语言和在其之上的生态的强烈反噬,其最主要的一些困难列举如下:

  • 某些 Java 语言的特性,使得 Graal VM 编译本地镜像的过程变得极为艰难。譬如常见的反射,除非使用安全管理器去专门进行认证许可,否则反射机制具有在运行期动态调用几乎所有 API 接口的能力,且具体会调用哪些接口,在程序不会真正运行起来的编译期是无法获知的。反射显然是 Java 不能放弃不能妥协的重要特性,为此,只能由程序的开发者明确地告知 Graal VM 有哪些代码可能被反射调用(通过 JSON 配置文件的形式),Graal VM 才能在编译本地程序时将它们囊括进来。
[
    {
        name: "com.github.fenixsoft.SomeClass",
        allDeclaredConstructors: true,
        allPublicMethods: true
    },
    {
        name: "com.github.fenixsoft.AnotherClass",
        fileds: [{name: "foo"}, {name: "bar"}],
        methods: [{
            name: "<init>",
            parameterTypes: ["char[]"]
        }]
    },
    // something else ……
]

这是一种可操作性极其低下却又无可奈何的解决方案,即使开发者接受不厌其烦地列举出自己代码中所用到的反射 API,但他们又如何能保证程序所引用的其他类库的反射行为都已全部被获知,其中没有任何遗漏?与此类似的还有另外一些语言特性,如动态代理等。另外,一切非代码性质的资源,如最典型的配置文件等,也都必须明确加入配置中才能被 Graal VM 编译打包。这导致了如果没有专门的工具去协助,使用 Graal VM 编译 Java 的遗留系统即使理论可行,实际操作也将是极度的繁琐。

  • 大多数运行期对字节码的生成和修改操作,在 Graal VM 看来都是无法接受的,因为 Substrate VM 里面不再包含即时编译器和字节码执行引擎,所以一切可能被运行的字节码,都必须经过 AOT 编译成为原生代码。请不要觉得运行期直接生成字节码会很罕见,误以为导致的影响应该不算很大。事实上,多数实际用于生产的 Java 系统都或直接或间接、或多或少引用了 ASM、CGLIB、Javassist 这类字节码库。举个例子,CGLIB 是通过运行时产生字节码(生成代理类的子类)来做动态代理的,长期以来这都是 Java 世界里进行类增强的主流形式,因为面向接口的增强可以使用 JDK 自带的动态代理,但对类的增强则并没有多少选择的余地。CGLIB 也是 Spring 用来做类增强的选择,但 Graal VM 明确表示是不可能支持 CGLIB 的,因此,这点就必须由用户(面向接口编程)、框架(Spring 这些 DI 框架放弃 CGLIB 增强)和 Graal VM(起码得支持 JDK 的动态代理,留条活路可走)来共同解决。自 Spring Framework 5.2 起,@Configuration 注解中加入了一个新的 proxyBeanMethods 参数,设置为 false 则可避免 Spring 对于非接口类型的 Bean 进行代理。同样地,对应在 Spring Boot 2.2 中,@SpringBootApplication 注解也增加了 proxyBeanMethods 参数,通常采用 Graal VM 去构建的 Spring Boot 本地应用都需要设置该参数。
  • 一切 HotSpot 虚拟机本身的内部接口,譬如 JVMTI、JVMCI 等,都将不复存在了——在本地镜像中,连 HotSpot 本身都被消灭了,这些接口自然成了无根之木。这对使用者一侧的最大影响是再也无法进行 Java 语言层次的远程调试了,最多只能进行汇编层次的调试。在生产系统中一般也没有人这样做,开发环境就没必要采用 Graal VM 编译,这点的实际影响并不算大。
  • Graal VM 放弃了一部分可以妥协的语言和平台层面的特性,譬如 Finalizer、安全管理器、InvokeDynamic 指令和 MethodHandles,等等,在 Graal VM 中都被声明为不支持的,这些妥协的内容大多倒并非全然无法解决,主要是基于工作量性价比的原因。能够被放弃的语言特性,说明确实是影响范围非常小的,所以这个对使用者来说一般是可以接受的。

以上,是 Graal VM 在 Java 语言中面临的部分困难,在整个 Java 的生态系统中,数量庞大的第三方库才是真正最棘手的难题。可以预料,这些第三方库一旦脱离了 Java 虚拟机,在原生环境中肯定会暴露出无数千奇百怪的异常行为。Graal VM 团队对此的态度非常务实,并没有直接硬啃。要建设可持续、可维护的 Graal VM,就不能为了兼容现有 JVM 生态,做出过多的会影响性能、优化空间和未来拓展的妥协牺牲,为此,应该也只能反过来由 Java 生态去适应 Graal VM。 为了推进 Java 生态向 Graal VM 兼容,Graal VM 主动拉拢了 Java 生态中最庞大的一个派系:Spring。从 2018 年起,来自 Oracle 的 Graal VM 团队与来自 Pivotal 的 Spring 团队已经紧密合作了很长的一段时间,共同创建了Spring Graal Native项目来解决 Spring 全家桶在 Graal VM 上的运行适配问题,用于微服务环境Spring Cloud 便会获得不受 Java 虚拟机束缚的更广阔的舞台空间。

5. Java的危与机

Java 与云原生的矛盾,来源于 Java 诞生之初,植入到它基因之中的一些基本的前提假设已经逐渐开始被动摇,甚至已经不再成立。 举个例子,每一位 Java 的使用者都听说过“一次编写,到处运行”(Write Once, Run Anywhere)这句口号。20 多年前,Java 成熟之前,开发者如果希望程序能够在 Linux、Solaris、Windows 等不同平台,在 x86、AMD64、SPARC、MIPS、ARM 等不同指令集架构上都能正常运行,就必须针对每种组合,编译出对应的二进制发行包,或者索性直接分发源代码,由使用者在自己的平台上进行编译。面对这个问题,Java 通过语言层虚拟化 的方式,令每一个 Java 应用都自动取得平台无关(Platform Independent)、架构中立(ArchitectureNeutral)的先天优势,让同一套程序格式得以在不同指令集架构、不同操作系统环境下都能运行且得到一致的结果,不仅方便了程序的分发,还避免了各种平台下内存模型、线程模型、字节序等底层细节差异对程序编写的干扰。在当年,Java 的这种设计带有令人趋之若鹜的强大吸引力,直接开启了托管语言(Managed Language,如 Java、.NET)一段兴盛期。在那个年代,是Java开历史之先河屏蔽不同操作系统底层差异,可以让其程序导出运行的底座般的差异。 面对相同的问题,今天的云原生选择以操作系统层虚拟化的方式,通过容器实现的不可变基础设施去解决。不可变基础设施这个概念出现得比云原生要早,原本是指该如何避免由于运维人员对服务器运行环境所做的持续的变更而导致的意想不到的副作用。但在云原生时代,它的内涵已不再局限于方便运维、程序升级和部署的手段,而是升华为一种向应用代码隐藏环境复杂性的手段,是分布式服务得以成为一种可普遍推广的普适架构风格的必要前提。将程序连同它的运行环境一起封装到稳定的镜像里,现已是一种主流的应用程序分发方式。Docker 同样提出过“一次构建,到处运行”(Build Once, Run Anywhere)的口号,尽管它只能提供环境兼容性和有局限的平台无关性(指系统内核功能以上的 ABI 兼容),且完全不可能支撑架构中立性,所以将“一次构建,到处运行”与“一次编写,到处运行”对立起来并不严谨恰当,但是无可否认,今天 Java 技术“一次编译,到处运行”的优势,已经被容器大幅度地削弱,已不再是大多数服务端开发者技术选型的主要考虑因素了。 如果仅仅是优势的削弱,并不足以成为 Java 的直接威胁,充其量只是一个潜在的不利因素,但更加迫在眉睫的风险来自于那些与技术潮流直接冲突的假设。譬如,Java 总体上是面向大规模、长时间的服务端应用而设计的,严(luō)谨(suō)的语法利于约束所有人写出较一致的代码;静态类型动态链接的语言结构,利于多人协作开发,让软件触及更大规模;即时编译器、性能制导优化、垃圾收集子系统等 Java 最具代表性的技术特征,都是为了便于长时间运行的程序能享受到硬件规模发展的红利。 另一方面,在微服务的背景下,提倡服务围绕业务能力而非技术来构建应用,不再追求实现上的一致,一个系统由不同语言,不同技术框架所实现的服务来组成是完全合理的;服务化拆分后,很可能单个微服务不再需要再面对数十、数百 GB 乃至 TB 的内存;有了高可用的服务集群,也无须追求单个服务要 7×24 小时不可间断地运行,它们随时可以中断和更新。同时,微服务又对应用的容器化亲和性,譬如镜像体积、内存消耗、启动速度,以及达到最高性能的时间等方面提出了新的要求,在这两年的网红概念 Serverless 也进一步增加这些因素的考虑权重,而这些却正好都是 Java 的弱项:哪怕再小的 Java 程序也要带着完整的虚拟机和标准类库,使得镜像拉取和容器创建效率降低,进而使整个容器生命周期拉长。基于 Java 虚拟机的执行机制,使得任何 Java 的程序都会有固定的基础内存开销,以及固定的启动时间,而且 Java 生态中广泛采用的依赖注入进一步将启动时间拉长,使得容器的冷启动时间很难缩短。 软件工业中已经出现过不止一起因 Java 这些弱点而导致失败的案例,如 JRuby 编写的Logstash,原本是同时承担部署在节点上的收集端(Shipper)和专门转换处理的服务端(Master)的职责,后来因为资源占用的原因,被Elstaic.co用 Golang 的Filebeat代替了 Shipper 部分的职能;又如 Scala 语言编写的边车代理Linkerd,作为服务网格概念的提出者,却最终被Envoy所取代,其主要弱点之一也是由于 Java 虚拟机的资源消耗所带来的劣势。 虽然在云原生时代依然有很多适合 Java 发挥的领域,但是具备弹性与韧性,随时可以中断重启的微型服务的确已经形成了一股潮流,在逐步蚕食大型系统的领地。正是由于潮流趋势的改变,新一代的语言与技术尤其重视轻量化和快速响应能力,大多又重新回归到了原生语言(Native Language,如 Golang、Rust)之上。

  • Java的重量级线程(其本身的设计就是为了计算密集型的服务型程序而设计的)
  • Java的对象头(在多线程模型的编码过程显得十分繁复和啰嗦 且占用内存资源)
  • Java的启动速度(由于内置的即时编译器、性能制导优化、垃圾收集子系统等都会拖慢整体的运行速度)

目前jdk19已经支持了n:m的多线程模型,graalvm目前oracle在积极布局研发,期待云原生时代Java能够继续像Java1(Web Applets)和Java2(Java se/ee/me以及 JavaCard)那样出来适合当下环境的里程碑式的版本。

参考链接:

  1. 周老师的凤凰架构