Java AOT之GraalVM native image介绍以及简单长连接服务实践

Java语言有20多年的发展历史,拥有众多优秀的特性如面向对象、安全、解释性、平台无关等,该语言以及其强大的生态使其成为最重要的网络编程语言。但是随着近年来技术架构的发展,微服务逐渐趋向云原生及Serverless化,使得Java也面临如下挑战:启动缓慢、内存占用大、预热问题。

Java服务启动时首先要启动一个JVM虚拟机,然后虚拟机会加载字节码,中间还包括类的加载解析初始化。JVM运行字节码运行时有解释执行和编译两种执行方式:当系统刚启动时,JVM会以解释执行并检测热点代码,热点代码会通过c1c2编译器进行编译成本地二进制代码。通过这样的机制,Java实现了很多诸如反射、动态代理等运行时的机制,例如可以在程序运行过程中加载并编译一段代码,而这个在静态编译中却不可想象。运行时编译还有个好处是可以做比较激进的编译优化,通过c2编译器和分支预测Java在某些场景下可以表现出比c++更强劲的性能。但这些优点也是有价的,包括启动耗时、预热耗时以及服务内存占用。

Graalvm

Graalvm是Oracle公司提供的一个高性能、云原生、多语言的虚拟机。除了运行 Java 和基于 JVM 的语言之外,GraalVM 的语言实现框架 (Truffle)使得在 JVM 上运行 JavaScript、Ruby、Python 和许多其他流行语言成为可能。借助 GraalVM Truffle,Java 和其他支持的语言可以直接互操作,并在同一内存空间中来回传递数据。

Java AOT之GraalVM native image介绍以及简单长连接服务实践_第1张图片

Native Image是一种将Java代码提前编译为独立可执行文件的技术,此刻执行文件包括应用程序类、依赖、运行时库以及JDK静态连接的本机代码。Graalvm通过子模块SubstrateVM来支持Native Image,相比JVM其生成的程序具有更快的启动时间和更低的运行时开销。

SubstrateVM

SubstrateVM是GraalVM实现静态编译的基础,可以从支持静态编译以及运行时两方面来简单了解。

静态编译

应用程序、第三方库和JDK字节码共同组成了静态编译的输入,SubstrateVM会对输入进行静态分析,找到其中可达代码,然后可达代码将会有静态编译器进行编译,最终得到native image。值得注意的是由于只会编译可达的代码,所以其生成的文件相对会较小。静态分析输出是控制流图(Control Flow Graph)和类型流图(Type Flow Graph),其耗时也为整个编译流程中最长。

运行时

Native image运行需要提供垃圾回收、类初始化检查、异常处理、多线程等支持。SubstrateVM通过Java做轻量化VM运行时实现。并且通过静态编译,将运行时支持一起编译至native image中,需要注意的是GraalVM社区版只提供SerialGC垃圾收集器。

实践

通过安装使用GraalVM社区版,并创建一个echo的长连接服务来实践GraalVM native image,这里的环境是MacOS。

安装GraalVM社区版

  1. 通过Github下载GraalVM

  2. 解压到/Library/Java/JavaVirtualMachines目录下

  3. 打开JavaVirtualMachines目录下Contents/Home/bin校验java -version,添加JAVA_HOME环境变量

  4. 安装native-imagegu

    gu install native-image,编译native image依赖于本地工具链,确保本地有安装glibc-devel、zlib-devel、gcc

长链服务

服务通过Netty实现一个echo服务器,主要包括以下类

启动类

Java AOT之GraalVM native image介绍以及简单长连接服务实践_第2张图片

Handle

Java AOT之GraalVM native image介绍以及简单长连接服务实践_第3张图片

Decoder

Java AOT之GraalVM native image介绍以及简单长连接服务实践_第4张图片

Encoder

Java AOT之GraalVM native image介绍以及简单长连接服务实践_第5张图片

pom.xml

Java AOT之GraalVM native image介绍以及简单长连接服务实践_第6张图片

Java AOT之GraalVM native image介绍以及简单长连接服务实践_第7张图片

Java AOT之GraalVM native image介绍以及简单长连接服务实践_第8张图片

先启动服务,使用nc工具测试功能,结果如下。

然后关闭服务器,用GraalVM执行静态编译,这里使用的是maven插件,执行mvn -Pnative -DskipTests package即可完成字节码编译及静态编译,编译结果如下,可以看到整个静态编译耗时还是比较长的,主要分布在静态分析和编译这两个阶段。

Java AOT之GraalVM native image介绍以及简单长连接服务实践_第9张图片

生成的native文件位于target包中,大小约17M,进入target目录,可用./mini-connector直接执行,用nc测试发送hello然后关闭服务器,结果如下

Java AOT之GraalVM native image介绍以及简单长连接服务实践_第10张图片

结论

通过GraalVM native image,可以将java服务启动时间压缩数十倍,且生成的二进制文件大小也优于包括所有依赖的jar包。

与传统Java运行模型相比,静态编译运行通过AOT避免了JIT的CPU开销,也避免了传统运行模型中一定存在的解释执行问题,使得程序性能较稳定。通过轻量化SubstrateVM实现,且也静态编译至native image中,提供了较快的vm性能和启动速度。

但是,任何技术都有优缺点。而Graalvm静态编译则需要面临解决动态类加载、反射、动态代理等动态特性的适配问题。另外通过native运行的程序,将不再适用面向传统JVM程序的调试、监控、Agent等功能。



Java AOT之GraalVM native image介绍以及简单长连接服务实践_第11张图片

 

你可能感兴趣的:(侦探工作笔记,java)