导读

JIT(Just-in-Time,实时编译)一直是Java语言的灵魂特性之一,与之相对的AOT(Ahead-of-Time,预编译)方式,似乎长久以来和Java语言都没有什么太大的关系。但是近年来随着Serverless、云原生等概念和技术的火爆,Java JVM和JIT的性能问题越来越多地被诟病,在Golang、Rust、NodeJS等新一代语言的包夹下,业界也不断出现“云原生时代,Java已死”的言论。那么,Java是否可以使用AOT方式进行编译,摆脱性能的桎梏,又是否能够在云原生时代焕发新的荣光?本文会带着这样的疑问,去探索Java AOT技术的历史和现状。

上上篇有讲过,HotSpot JVM中集成了两种JIT编译器,Client Compiler和Server Compiler,它们的作用也不同。Client Compiler注重启动速度和局部的优化,Server Compiler则更加关注全局的优化,性能会更好,但由于会进行更多的全局分析,所以启动速度会变慢。两种编译器有着不同的应用场景,在虚拟机中同时发挥作用。而随着时间的发展,不论是Client Compiler还是Server Compiler都发展出了各具特色的实现,如 C1、C2、Graal Compiler等,你可以在JVM启动参数中选择自己所需的JIT编译器实现。

从JDK 10起,HotSpot虚拟机同时拥有三种不同的即时编译器。此前我们已经介绍了经典的客
户端编译器和服务编译,还有全新的即时编译器:Graal编译器。

JIT与AOT的区别

提前编译是相对于即时编译的概念,提前编译能带来的最大好处是Java虚拟机加载这些已经预编译成二进制库之后就能够直接调用,而无须再等待即时编译器在运行时将其编译成二进制机器码。理论上,提前编译可以滅少即时编译带来的预热时间,减少Java应用长期给人带来的“第一次运行慢"的不良体验,可以放心地进行很多全程序的分析行为,可以使用时间压力更大的优化措施。但是提前编译的坏处也很明显,它破坏了Java"—次编写,到处运行"的承诺,必须为每个不同的硬件、操作系统去编译对应的发行包;也显著降低了Java链接过程的动态性,必须要求加载的代码在编译期就是全部已知的,而不能在运行期才确定,否则就只能舍弃掉己经提前编译好的版本,退回到原来的即时编译执行状态。

AOT的优点

  • 在程序运行前编译,可以避免在运行时的编译性能消耗和内存消耗
  • 可以在程序运行初期就达到最高性能,程序启动速度快
  • 运行产物只有机器码,打包体积小

AOT的缺点

  • 由于是静态提前编译,不能根据硬件情况或程序运行情况择优选择机器指令序列,理论峰值性能不如JIT
  • 没有动态能力
  • 同一份产物不能跨平台运行

Java AOT的历史演进

JIT是Java的一大灵魂特性,得益于即时编译,Java语言同时拥有了不输编译型语言的运行速度和“一次编译、到处运行”的跨平台能力、甚至还拥有和解释型语言类似的动态性能力,可以说JIT是Java语言能够快速风靡全球并得到广泛应用的重要原因之一。因此在Java诞生至今的几十年里,AOT编译方式和Java可以说是“一毛钱关系都没有”,那么为什么今天我们又要提起以AOT的方式运行Java程序呢,是JIT它不香么?

其实Java本身一直存在着一些“问题”:JVM本身是很重的,因此对服务器的性能消耗(某种意义上可以说是性能浪费)是很高的,同时Java应用的启动速度也往往被人所诟病。但是这些问题在Java所带来的跨平台运行能力和动态特性面前,都是“值得的牺牲” —— 使用Java你可以更方便的进行代码的打包和交付,可以轻松写出性能不差的程序并部署在任何主流的OS上。这些对于企业用户而言,一直是技术选型非常重要的考量因素,直到Docker和Serverless的诞生,逐渐改写了这一切:

Docker的诞生,让底层运行环境变得可以随意定制,你可以在生产环境的任何一台服务器上轻松混部Windows和Linux的各种发行版,这让JVM的跨平台能力显得不那么重要了。

Serverless概念的火爆,让弹性伸缩能力成为服务端程序的一大重要目标,这时候JVM的“臃肿”和JIT导致的启动延迟就让Java程序显得很不“Serverless”(拉包时间长、启动速度慢),如今我们提到云原生总是先想到Go、NodeJS而不是Java,似乎Java和云原生已经不是一个时代的产物了。

所以如果想让Java在云原生时代焕发“第二春”,支持AOT是非常重要的一步,而在这一步上,Java语言却经历了一波三折:

2016年,OpenJDK的 JEP 295提案首次在Java中引入了AOT支持,在这一草案中,JDK团队提供了 jaotc 工具,使用此工具可以将指定class文件中的方法逐个编译到native代码片段,通过Java虚拟机在加载某个类后替换方法的的入口到AOT代码来实现启动加速的效果。

jaotc的类似于给JVM打了一个“补丁”,让用户有权利将部分代码编译成机器码的时期提前,并预装载到JVM中,供运行时代码调用。不过这个补丁存在很多问题:

首先是在设计上没有考虑到Java的多Classloader场景,当多个Classloader加载的同名类都使用了AOT后,他们的static field是共享的,而根据java语言的设计,这部分数据应该是隔开的。由于这个问题无法快速修复,jaotc最终给出的方案只是暴力地禁止用户自定义classloader使用AOT。

此外,由于社区人手不足,缺乏调优和维护,jaotc的实际运行效果不尽人意,有时甚至会对应用的启动和运行速度带来反向优化,实装没多久之后就退化为实验特性,最终在JDK 16中被删除,结束了短暂的一生。

后来阿里AJDK团队自研的AppCDS(Class-Data-Share)技术继承了jatoc的思路,进行了大幅的优化和完善,目前也不失为一种Java AoT的选择,其本质思路和jaotc基本一致 ,这里就不再赘述了。

而目前业界除了这种在JVM中进行AOT的方案,还有另外一种实现Java AOT的思路,那就是直接摒弃JVM,和C/C++一样通过编译器直接将代码编译成机器代码,然后运行。这无疑是一种直接颠覆Java语言设计的思路,不过还是被各路大佬们实现了,那就是GraalVM Native Image。它通过C语言实现了一个超微缩的运行时组件 —— Substrate VM,基本实现了JVM的各种特性,但足够轻量、可以被轻松内嵌,这就让Java语言和工程摆脱JVM的限制,能够真正意义上实现和C/C++一样的AOT编译。这一方案在经过长时间的优化和积累后,已经拥有非常不错的效果,基本上成为Oracle官方首推的Java AOT解决方案,接下来我们会重点分析一下这项技术的原理和实际应用。

新的破局点GraalVM

先说一下GraalVM,这是Oracle在2019年推出的新一代UVM(通用虚拟机),它在HotSpotVM的基础上进行了大量的优化和改进,主要提供了两大特性:

  • Polyglot:多语言支持,你可以在GraalVM中无缝运行多种语言,包括Java、JS、Ruby、Python甚至是Rust。更重要的是可以通过GraalVM的API来实现语言混编 —— 比如在一段Java代码中无缝引用并调用一个Python实现的模块。
  • HighPerformance:高性能,首先它提供了一个高性能的JIT引擎,让Java语言在GraalVM上执行的时候效率更高速度更快 ;其次就是提供了SubstrateVM,通过Graal Compiler你可以将各种支持的语言(包括Java)编译成本地机器代码,获得更好的性能表现。

值得一提的是,Substrate VM虽然名为VM,但并不是一个虚拟机,而是一个包含了 垃圾回收、线程管理 等功能的运行时组件(Runtime Library),就好比C++当中的stdlib一样。当Java程序被编译为Native Image运行的时候,并不是运行在Substrate VM里,而是将SubstrateVM当作库来使用其提供的各种基础能力,以保障程序的正常运行。

不难看出,GraalVM这个项目的野心是非常大的,可以说这个项目是Oracle抢占云原生市场的一个重要布局,随着官方的不断投入和社区的壮大,目前GraalVM已经日渐成熟,在高性能和跨语言支持方面都交出了令人满意的答卷。GraalVM本身是一个非常庞大的项目,有很多的细节点可以深挖,不过接下来我们还是重点研究一下它的AOT能力 —— Native Image。

Native Image:原理与限制

一个Java程序究竟是如何被编译成静态可执行文件的?我们先来看一下NativeImage的原理。

Native Image的输入是整个应用的所有组件,包括应用本身的代码、各种依赖的库、JDK库、以及SVM;首先会进行整个应用的初始化,也就是代码的静态分析,这个分析过程有点类似GC中的“可达性分析”,会讲程序运行过程中将所有可达的代码、变量、对象生成一个快照,最终打包成一个可执行的Native Image。

一个完整的Native Image包含两个部分,一部分称为 Text Section,即用户代码编译成的机器代码;另一部分称为 Data Section,存储了应用启动后堆区内存中各种对象的快照。

可以预见的是,这个静态分析的过程(官方称之为 Pionts-to Analysis)是非常复杂且耗时的,整个分析过程会以递归的方式进行,最终得到两个树形结构Call Tree(包含所有可达的方法)以及Object Tree(包含所有可达的对象),Call Tree中所包含的方法会被AOT编译为机器码,成为Native Image的Text Section,而Object Tree中所包含的对象及变量则会被保存下来,写入Native Image的Data Setion。

整个静态分析的算法非常复杂,目前网上相关的资料也较少,如果有对具体算法感兴趣的同学,官方团队在Youtube上有一个相对比较详细的算法说明视频,可以自行查看:https://www.youtube.com/watch?v=rLP-8q3Cb8M

作为一个Java程序员,你一定会好奇JVM的动态特性,例如反射、代理,要如何进行静态分析呢?很显然,这两者之间是存在冲突的,因此Native Image设置了一个名为“Closed World”的假设作为静态分析的基本前提。

这个基本前提包含三个要求,对应的也就是目前Native Image存在的三个限制:

  1. Points-to分析的时候,需要接受完整的字节码作为输入(即项目中所有用到的class的字节码都需要获取的到)。

=> 在运行期动态生成或者是动态获取字节码的程序,无法构建成 Native Image。

  1. Java的动态特性,包括反射、JNI、代理,都需要通过配置文件在构建前实现声明好。

=> 无法提前声明动态特性使用范围的程序,无法构建成Native Image (例如,根据用户输入的一个参数反射去调用某个方法)。

  1. 在整个运行过程中,程序不会再加载任何新的class。

=> 在运行期间执行动态编译,或者是自定义Classloader动态装载类的程序,无法构建成Native Image。

Native Image:环境安装

GraalVM安装

Native Image:实践

介绍了Native Image的基本原理和限制后,让我们来实际实践看看这项技术到底能够带给我们什么。

这里我们先给出一个非常基础的DEMO代码:

public class HelloWorld {private static final String CONST = "this-is-a constant var";private String name;public HelloWorld(String name) {this.name = name;}public void sayHello() {System.out.println("hello, " + name);}public static void main(String[] args) {System.out.println(CONST);HelloWorld h1 = new HelloWorld("lumin");HelloWorld h2 = new HelloWorld(args[0]);h1.sayHello();h2.sayHello();}
}

如何将这段代码构建成Native Image呢?首先安装好GraalVM,然后先使用javac将代码编译成字节码:

$ javac HelloWorld.java

接下来执行Native Image Build,指定类名

$ native-image HelloWorld

整个构建过程会执行比较长的一段时间,主要是执行Points-Analysis过程较长(大约三分多钟),最终的产物就是一个二进制文件:

可以看到这个HelloWorld最终打包产出的二进制文件大小为8.2M,这是包含了SVM和JDK各种库后的大小,虽然相比C/C++的二进制文件来说体积偏大,但是对比完整JVM来说,可以说是已经是非常小了。

再对比下运行速度:

可以看到,相比于使用JVM运行,Native Image的速度要快上不少,cpu占用也更低一些,从官方提供的各类实验数据也可以看出Native Image对于启动速度和内存占用带来的提升是非常显著的:

接下来我们加上 -H:+PrintImageObjectTree -H:+ExhaustiveHeapScan -H:+PrintAnalysisCallTree的参数再进行一次build,这样可以将整个Points-to Analysis的详细过程(Object Tree和Call Tree)打印出来以供分析:

call_tree_xxx文件中会包含完整的方法调用树,可以看到是一个递归的树形结构

通过Call Tree就可以得到整个程序运行过程中所有可能用到的方法,这些方法的代码都会被编译为机器码。

object_tree_xxx文件中,则包含了代码中所有使用到的对象和变量:

这里存储的主要是各种静态对象和变量,它们最终都被被打包至Image Heap中。

最后我们再来看一个使用反射的例子:

public class HelloReflection {public static void foo() {System.out.println("Running foo");}public static void bar() {System.out.println("Running bar");}public static void main(String[] args) {for (String arg : args) {try {HelloReflection.class.getMethod(arg).invoke(null);}catch (ReflectiveOperationException ex) {System.out.println("Exception running" + arg + ": "+ ex.getClass ().getSimpleName());}}}
}

这段代码接收用户的输入作为入参,然后通过反射调用用户指定的方法,我们通过普通方式来编译执行这段代码,是可以正常Work的:

$ java HelloReflection foo bar
Running foo
Running bar
d:\test>native-image HelloReflection
========================================================================================================================
GraalVM Native Image: Generating 'helloreflection' (executable)...
========================================================================================================================
[1/7] Initializing...                                                                                    (5.9s @ 0.08GB)Version info: 'GraalVM 22.2.0 Java 11 CE'Java version info: '11.0.16+8-jvmci-22.2-b06'C compiler: cl.exe (microsoft, x64, 19.32.31332)Garbage collector: Serial GC
[2/7] Performing analysis...  [*****]                                                                    (6.9s @ 1.05GB)2,695 (73.98%) of  3,643 classes reachable3,437 (53.28%) of  6,451 fields reachable12,173 (45.34%) of 26,851 methods reachable26 classes,     0 fields, and   267 methods registered for reflection62 classes,    53 fields, and    52 methods registered for JNI access1 native library: version
[3/7] Building universe...                                                                               (1.0s @ 0.58GB)Warning: Reflection method java.lang.Class.getMethod invoked at HelloReflection.main(HelloReflection.java:14)
Warning: Aborting stand-alone image build due to reflection use without configuration.
Warning: Use -H:+ReportExceptionStackTraces to print stacktrace of underlying exception
------------------------------------------------------------------------------------------------------------------------0.7s (5.0% of total time) in 14 GCs | Peak RSS: 1.94GB | CPU load: 6.07
========================================================================================================================
Failed generating 'helloreflection' after 14.1s.
========================================================================================================================
GraalVM Native Image: Generating 'helloreflection' (executable)...
========================================================================================================================
[1/7] Initializing...                                                                                    (5.7s @ 0.08GB)Version info: 'GraalVM 22.2.0 Java 11 CE'Java version info: '11.0.16+8-jvmci-22.2-b06'C compiler: cl.exe (microsoft, x64, 19.32.31332)Garbage collector: Serial GC
[2/7] Performing analysis...  [*****]                                                                    (7.5s @ 0.32GB)2,807 (74.79%) of  3,753 classes reachable3,564 (53.47%) of  6,666 fields reachable12,667 (45.85%) of 27,625 methods reachable26 classes,     0 fields, and   272 methods registered for reflection62 classes,    53 fields, and    52 methods registered for JNI access1 native library: version
[3/7] Building universe...                                                                               (1.4s @ 0.80GB)
[4/7] Parsing methods...      [*]                                                                        (1.0s @ 1.53GB)
[5/7] Inlining methods...     [***]                                                                      (0.8s @ 0.46GB)
[6/7] Compiling methods...    [***]                                                                      (5.5s @ 1.06GB)
[7/7] Creating image...                                                                                  (1.7s @ 1.43GB)4.45MB (38.22%) for code area:     7,449 compilation units6.95MB (59.66%) for image heap:   90,863 objects and 5 resources252.45KB ( 2.12%) for other data11.64MB in total
------------------------------------------------------------------------------------------------------------------------
Top 10 packages in code area:                               Top 10 object types in image heap:664.30KB java.util                                          928.95KB byte[] for code metadata360.01KB java.lang                                          853.94KB java.lang.String353.50KB com.oracle.svm.jni                                 840.00KB byte[] for general heap data225.12KB java.util.regex                                    637.97KB java.lang.Class222.22KB java.text                                          526.25KB byte[] for java.lang.String207.03KB java.util.concurrent                               389.16KB java.util.HashMap$Node131.93KB com.oracle.svm.core.code                           352.09KB char[]117.02KB java.math                                          241.23KB com.oracle.svm.core.hub.DynamicHubCompanion110.77KB com.oracle.svm.core.genscavenge                    191.59KB java.util.HashMap$Node[]99.46KB sun.text.normalizer                                163.05KB java.lang.String[]1.96MB for 109 more packages                                1.41MB for 777 more object types
------------------------------------------------------------------------------------------------------------------------0.9s (3.4% of total time) in 17 GCs | Peak RSS: 3.19GB | CPU load: 7.38
------------------------------------------------------------------------------------------------------------------------
Produced artifacts:D:\test\helloreflection.build_artifacts.txt (txt)D:\test\helloreflection.exe (executable)
========================================================================================================================
Finished generating 'helloreflection' in 25.0s.
Warning: Image 'helloreflection' is a fallback image that requires a JDK for execution (use --no-fallback to suppress fallback image generation and to print more detailed information why a fallback image was necessary).d:\test>

但是如果我们通过native-image运行,则会出现问题,这里我们需要加上–no-fallback参数来构建,否则graalvm检测到这个程序使用了未配置的反射时,会把产物自动降级成jvm运行:

$ ./helloreflection foo
Exception runningfoo: NoSuchMethodException

可以看到,运行foo方法提示 NoSuchMethodException,这就是因为在编译时我们无法知道用户真正调用的会是哪个方法,因此静态编译的时候就不会把foo、bar这两个方法认为是“可达的”,最终的native image中也就不会包括这两个方法的机器码 。要解决这个问题,我们就需要进行 配置化的 提前声明

在编译的目录下新建一个reflect-config.json,格式内容如下:

[{"name": "HelloReflection","methods": [{"name":"foo", "parameterTypes": []}]}
]

这样就相当于显示地声明了HelloReflection的foo方法会被反射调用,native build的时候就会将这个方法编译为机器码并写入image当中。可以再看下运行效果:

编译日志

[7/7] Creating image...                                                                                  (1.6s @ 0.96GB)4.28MB (37.44%) for code area:     7,124 compilation units6.91MB (60.45%) for image heap:   89,515 objects and 5 resources246.55KB ( 2.11%) for other data11.44MB in total
------------------------------------------------------------------------------------------------------------------------
Top 10 packages in code area:                               Top 10 object types in image heap:635.07KB java.util                                          892.45KB byte[] for code metadata353.50KB com.oracle.svm.jni                                 840.44KB java.lang.String324.45KB java.lang                                          831.75KB byte[] for general heap data225.12KB java.util.regex                                    588.85KB java.lang.Class222.22KB java.text                                          516.40KB byte[] for java.lang.String166.94KB java.util.concurrent                               389.16KB java.util.HashMap$Node131.93KB com.oracle.svm.core.code                           352.09KB char[]117.02KB java.math                                          231.60KB com.oracle.svm.core.hub.DynamicHubCompanion110.77KB com.oracle.svm.core.genscavenge                    191.59KB java.util.HashMap$Node[]99.46KB sun.text.normalizer                                160.39KB java.lang.String[]1.90MB for 110 more packages                                1.39MB for 750 more object types
------------------------------------------------------------------------------------------------------------------------0.8s (3.4% of total time) in 17 GCs | Peak RSS: 3.24GB | CPU load: 7.12
------------------------------------------------------------------------------------------------------------------------
Produced artifacts:d:\test\helloreflection.build_artifacts.txt (txt)d:\test\helloreflection.exe (executable)
========================================================================================================================
Finished generating 'helloreflection' in 23.2s.

运行效果

# linux
$native-image -H:ReflectionConfigurationFiles=./reflect-config.json HelloReflection$./helloreflection foo
Running foo$./helloreflection bar
Exception runningbar: NoSuchMethodException# win10d:\test>helloreflection.exed:\test>helloreflection.exe foo
Running food:\test>helloreflection.exe foo bar
Running foo
Exception runningbar: NoSuchMethodExceptiond:\test>

可以看到,显示声明了的foo方法可以正常被调用,但是没有声明过的bar方法,依然会抛出NoSuchMethodException。

Spring Native

上面我们的实践都是比较简单的针对某一个Java Class而言,而我们实际线上使用的工程往往都要复杂许多,尽管Native Image也提供了编译一个完整jar包的能力,但是对于我们通常使用的spring、maven工程来说,由于反射和代理的存在,根本不可能直接通过Native Image编译成功,因此我们还需要工程框架层面的支持,否则Native Image永远无法成为一种生产工具,而更像一个玩具。

作为Java工程界的龙头大佬,Spring自然观察到了这一点,于是就有了Spring Native。

首先需要说明一下,Spring Native目前还属于实验特性,最新Beta版本为0.12.1,还没有推出稳定的1.0版本(按照官方预期是2022年内会推出),需要Spring Boot最低版本是2.6.6,后续Spring Boot 3.0中也会默认支持Native Image。

https://github.com/spring-projects-experimental/spring-native

可以看到活跃度还是不错的,现在处于适配和扩展的阶段。

那么,Spring Native给我们带来了什么呢?

首先是Spring框架的Native化支持,包括IOC、AOP等各种Spring组件及能力的Native支持;其次是Configuration支持,允许通过@NativeHint注解来动态生成Native Image Configuration(reflect-config.json, proxy-config.json等);最后就是Maven Plugin,可以通过Maven构建获得Native Image,而不需要再手动去执行native-image命令。

可以参考这篇Spring Native在Windows环境使用说明

手动支持

思路就是先打成jar包,然后native-image -cp spring-native-example-0.0.1-SNAPSHOT.jar
生成二进制文件

工程支持

接下来我们通过一个DEMO来简单入门Spring Native

首先确保Spring Boot的版本在2.6.6以上,然后在一个基础Spring Boot项目的基础上,引入以下依赖:


<dependency><groupId>org.springframework.experimental</groupId><artifactId>spring-native</artifactId><version>0.11.4</version>
</dependency>

接着引入plugin

<plugin><groupId>org.springframework.experimental</groupId><artifactId>spring-aot-maven-plugin</artifactId><version>0.11.4</version><executions><execution><id>generate</id><goals><goal>generate</goal></goals></execution><execution><id>test-generate</id><goals><goal>test-generate</goal></goals></execution></executions>
</plugin>

最后指定native build的profile

<profiles><profile><id>native</id><dependencies><!-- Required with Maven Surefire 2.x --><dependency><groupId>org.junit.platform</groupId><artifactId>junit-platform-launcher</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.graalvm.buildtools</groupId><artifactId>native-maven-plugin</artifactId><version>0.9.11</version><extensions>true</extensions><executions><execution><id>build-native</id><goals><goal>build</goal></goals><phase>package</phase></execution><execution><id>test-native</id><goals><goal>test</goal></goals><phase>test</phase></execution></executions><configuration><!-- ... --></configuration></plugin><!-- Avoid a clash between Spring Boot repackaging and native-maven-plugin --><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><classifier>exec</classifier></configuration></plugin></plugins></build></profile>
</profiles>

引入之后,运行mvn -Pnative -DskipTests clean package命令,就可以进入native build过程,编译完成后产物在 target/{your-app-name}。

可以看下启动运行的效果:

相比于普通JVM方式运行,启动速度大约提升了5倍(1.2s -> 0.2s)。

对于大部分简单的Spring Boot应用,只需要经过上述这些简单的配置就可以完整运行了,看起来似乎很美好,是不是?但这仅仅是对于Spring组件而言,Native Image目前需要面对的最大问题,还是来自于Java世界数以万计的各种库:Netty、fastjson、logback、junit … 尽管很多的开源库都开始改造以支持Native Build,但对于生产环境的企业级应用来说,依然还有很长的路要走(当然,这可能也不是Native Image最适用的场景)。

小结

最后对Java的AOT方案做一个总结。Java AOT在经过一波三折的发展后,目前最为成熟可行的方案就是 GraalVM Native Image,它所带来的优势是显著的:更快的启动速度、更小的内存消耗、脱离JVM独立运行 。但对应的,它也存在着非常多的限制,尤其是在充满了反射等动态特性的Java工程生态圈,很难得到大规模的广泛应用。

总的来说,Java AOT目前是有着明确使用场景的一种技术,主要可以应用于:

  1. 编写命令行CLI程序,希望程序可以完整独立运行而不是需要额外安装JVM。
  2. 运行环境资源严重受限的场景,例如IoT设备、边缘计算等场景。
  3. 希望追求极致的启动速度,并且应用逻辑相对足够轻量,如FaaS。

当然未来Java AOT仍然会进一步发展,我们可以拭目以待。说不定能和go扳手腕就看这个

附录

《graal vm 22.1版本官方文档》

《Spring Native官方文档》

Java在云原生的破局利器——AOT(JIT与AOT)相关推荐

  1. “记忆力争夺战”走向尾声,“潜意识记忆营销”将成破局利器

    文 | 公关之家 作者│方韵 引言:营销的发展史其实就是消费者与营销方的博弈史.信息时代营销面临的最大困境就是,消费者已经没有多余的记忆空间留给新品牌了.这时,"潜意识记忆营销"将 ...

  2. Java应用云原生构建优化

    layout: post title: Java应用云原生构建优化 catalog: true tag: [云原生, Devops, K8s, Java] 1. 面临的问题 2. Jib调研 2.1. ...

  3. 剑指企业级云原生,阿里云 CNFS 如何破局容器持久化存储困境

    简介: 云原生趋势下,应用容器化比例正在快速增长,Kubernetes 也已成为云原生时代新的基础设施. 据 Forrester 预测,到 2022 年, 全球企业及组织在生产环境运行容器化应用.观察 ...

  4. 新一代智慧城市建设的破局利器--城市大脑

    在智慧城市的建设浪潮中,大数据分析.云计算.AI人工智能等全新一代信息技术交错纵横,在不断整合创新中"城市大脑"成为了浪潮中大家竞相追赶的灯塔. 近些年来,城市大脑.城市云脑.城市 ...

  5. 城市大脑系统的创建能否作为智慧城市新时代的破局利器

    在智慧城市的建设浪潮中,大数据.云计算.人工智能等全新一代信息技术交错纵横,在不断融合创新中"城市大脑"成为了浪潮中大家竞相追赶的灯塔. 近些年来,城市大脑.城市云脑.城市超级大脑 ...

  6. 进击的 Java ,云原生时代的蜕变

    点击蓝色"程序猿DD"关注我 回复"资源"获取独家整理的学习资料! 作者 | 易立 来源 | 公众号「阿里巴巴云原生」 导读:云原生时代的来临,与Java 开发 ...

  7. 阿里技术专家:进击的 Java ,云原生时代的蜕变

    作者| 易立 阿里云资深技术专家 导读:云原生时代的来临,与Java 开发者到底有什么联系?有人说,云原生压根不是为了 Java 存在的.然而,本文的作者却认为云原生时代,Java 依然可以胜任&qu ...

  8. Java基础学习总结(185)—— Java 在云原生时代的进化

    前言 在企业软件领域,Java 依然是绝对王者,但它让开发者既爱又恨.一方面因为其丰富的生态和完善的工具支持,可以极大提升了应用开发效率:但在运行时效率方面,Java 也背负着"内存吞噬者& ...

  9. java边界布局东南西北_破局!发展大南昌格局!城市向南生长或是关键!

    城市的生长离不开疆域扩张,如今的南昌城市版图已经形成一定规模,在新中国成立后,七十余年沧桑巨变,这座城市的城镇化不仅让大面积区域从"乡土"转变为"城市",也让城 ...

最新文章

  1. css盒子教程,彻底弄懂css盒子模式(div布局快速入门)_css教程
  2. ExtJs中表格用例代码
  3. docker报错:OCI runtime create failed...process_linux.go:449: container init caused “write /proc/self/
  4. 使用BootStrap编写网页,如何设置全屏页面背景?
  5. 经典滑盖全面屏手机大降价!荣耀Magic2到手1799元
  6. Linux-Can't create socket: Too many open files
  7. 跟我一起学Oracle 11g【1】----基础回顾
  8. ecshop 支持 php,ecshop支持PHP7的修改方法
  9. PyTorch搭建简单神经网络实现回归和分类
  10. Ubuntu21.04安装网易有道词典
  11. 物联网技术如何驱动大数据
  12. ai的预览模式切换_ai模式切换快捷键是什么啊?ai模式切换方法
  13. 一文详解被阿里腾讯视作核心机密的大数据平台架构
  14. HEVC帧内预测参考像素检测获取和滤波
  15. MATLAB仪表表盘数字识别
  16. 【解决方案】t2gp.exe - 损坏的映像 | libcef.dll没有被指定在 Windows 上运行
  17. MS2108 RGB转USB 数字视频采集芯片 支持8bit/16bit BT601/BT709/BT656/ BT1120转USB2.0
  18. redmi ax6s刷机体验和救砖
  19. 阿里专家精心整理分享的Java程序员面试笔试通关宝典PDF
  20. 我的世界服务器物品修改器,ReadyEditor全备编辑————基于箱子菜单的物品编辑器[1.14+]...

热门文章

  1. Android出现“XXX 无响应”分析与解决方案
  2. SWFupload上传插件案例及头像的截取
  3. CSS文本与字体知识梳理
  4. mysql time类型解析_mysql时间数据类型解析
  5. java 坑_JAVA陷阱---三元表达式潜藏的坑
  6. Python:中文分词库jieba安装使用
  7. 记一次SQL server的驱动配置
  8. Spring自动装配机制详解
  9. java项目使用说明_java项目 BeanCopier使用说明
  10. Java中I/O(输入/输出)的操作