技术标签: jvm spring boot java Spring Boot 3.x
有道无术,术尚可求,有术无道,止于术。
本系列Spring Boot版本3.0.3
源码地址:https://gitee.com/pearl-organization/study-spring-boot3
本篇介绍如何使用GraalVM
构建原生镜像。
在开发Spring Boot
应用或者其他JAVA
程序的过程中,启动慢、内存占用大是比较头疼的问题,往往需要更多的资源去部署,成本大幅提高。为了优化上述问题,常常使用优化程序、使用更小消耗的JVM、使用容器等措施。
现在有一个叫做Native Image
(原生镜像)的技术,可以将JAVA
应用的字节码直接编译为本地机器码,打包成本地可执行文件,运行应用时无需Java
虚拟机进行动态编译,因此启动速度很快、内存消耗也很低。
在正式介绍Native Image
之前,我们需要了解下JIT
和 AOT
的相关概念。
通常程序有两种运行方式:
JIT
是Just-in-time
的缩写,一般称为即时编译或动态编译。Java
源代码在运行的过程中,类加载器将需要运行的字节码处理并分配到内存中,然后JVM
执行引擎需要调用解释器将字节码翻译为计算机能执行的机器码,最后执行机器指令。
需要解释执行势必会造成运行效率降低,为了提高执行速度,JAVA
引入了 JIT
编译器。当某个方法或代码块运行特别频繁的时候,JVM
会将其标注为热点代码, JIT
编译器会将热点代码编译成本地机器相关的机器码,优化后进行缓存,下次执行这些代码时,直接调用缓存中的机器码,无需重复解释,在一定程度上提高了执行速度。
JAVA
一直在努力提高启动和运行时性能,希望其能够在更广泛的场景达到或接近本地语言的性能。虽然引入了JIT
编译器,但是需要花费较长时间才能热身完,而且有些Java
方法还没法编译,性能方面也会下降。
AOT
是Ahead-of-Time
的缩写,一般称为静态编译。程序在执行前全部被翻译为机器码,可以直接运行二进制文件,比如C++
就是使用静态编译。
在 JDK 9
中, AOT
作为实验特性被引入,只支持 java.base
模块可以编译成AOT
库,使用jaotc
工具将Java
类文件编译为本机代码,避免了 JIT
预热等各方面的开销。但是实际运行效果不尽人意,最终在JDK 16
中被删除。
在JDK 17
中,也移除了实验性的AOT
与JIT
,彻底拥抱GraalVM
实现静态编译。
在当前微服务、云原生盛行的时代,JAVA 程序
显得越来越臃肿,虽然使用AOT
也有诸多缺点,比如打包时间长、舍弃平台无关性、反射、JNI、动态代理的分析能力有限。但是JAVA
必定会向AOT
发展,否则在云原生时代,可以能被其他后起之秀慢慢蚕食市场。
GraalVM
是一个高性能跨语言虚拟机,其目的是提升Java
和其他JVM
语言编写程序的执行速度,同时也为JavaScript
、Python
和许多其他流行语言提供运行时环境。起始于 2011 年 Oracle
实验室的一个研究项目。
GraalVM
三大核心:
Java
虚拟机提供高性能的JIT
编译器AOT
编译器,提前将 Java
字节码编译为本机机器码。GraalVM
的Truffle
语言实施框架可与 GraalVM
编译器协作,以卓越性能运行 JavaScript、Python、Ruby
以及 JVM
支持的其他语言。GraalVM
提供了两种运行Java
应用程序的方法:
HotSpot JVM
上使用实时JIT
编译器AOT
将Java
应用程序编译的本地可执行文件社区版和企业版的一些区别:
Open JDK
,由社区组织维护GraalVM
的优点:
帮助开发人员显著提升应用的性能效率,同时降低IT
成本。
构建现代 Java
应用,通过微服务和容器来满足云原生需求,微服务是执行单一功能的小型、独立微应用。在现实中,业务应用通常要使用数百项服务,每项服务都需要快速启动,以尽可能降低延迟和云使用成本。
可以构建一个各种编程语言基于单一 JVM
运行的生态系统,提高开发效率。
GraalVM
的缺点:
Java
的平台无关性,编译为本地执行文件,不同操作系统的服务器,编译出来的文件不一样,比如 Windows
编译出来的文件,并不能在Linux
系统运行,也就让JAVA
丢失了平台无关性。JAVA
设计之初,一次编译、到处运行是其最重要的特性,但是现在容器技术的出现,该特性显得很牵强。CGLIB
动态代理这些和字节码打交道的机制,是在程序运行时动态调用,无法经过 AOT
编译成原生代码,构建时还需要提供各种配置文件去适配。GraalVM
提供了多种操作模式。
1、JVM运行时模式
在HotSpot JVM
上运行程序时,默认使用GraalVM
编译器作为顶级JIT
编译器。在运行时,应用程序在JVM上
正常加载和执行。JVM
将字节码传递给编译器,编译器将其编译为机器代码并将其返回给JVM
。
2、原生镜像
Native Image
是一种创新技术,它将Java
代码编译为独立的本地可执行文件或本地共享库。在构建本机可执行文件期间,处理的Java
字节码包括所有应用程序类、依赖项、依赖于第三方的库以及所需的任何JDK类。生成的本地可执行文件特定于每个操作系统和机器体系结构,并不需要JVM
。
从当前GraalVM 22.1
支持的功能图可以看出,Native Image
可以在AMD6
、AArch64
等服务器平台生成环境使用。
3、Java on Truffle
Java on Truffle
是一个 JVM
实现,它使用了 Truffle
多语言执行框架。提供了Java
虚拟机所有的核心组件,实现了与Java
运行时环境库相同的API
,并重用GraalVM中
的所有JAR
和本机库。支持多语言互操作,例如,JavaScript
程序可以运行Ruby
方法,无需制作副本就能共享数值。基于JVM
运行时,Truffle
能够与GraalVM
编译器协作,将受支持语言编译为本机机器码,从而优化性能。
了解了GraalVM
之后,我们着重了解并使用Native Image
技术将一个Spring Boot 3
项目编译为一个可执行二进制文件。
Native Image
:是一种将Java
代码提前编译为二进制文件的技术,即本机可执行文件。本机可执行文件只包含运行时所需的代码,即应用程序类、标准库类、语言运行时以及来自JDK的静态链接本机代码。
Native Image
处理应用程序类和其他元数据,以创建特定操作系统和体系结构的二进制文件。首先,本地镜像工具对代码执行静态分析,以确定应用程序运行时可访问的类和方法。其次,它将类、方法和资源编译成二进制文件。整个过程被称为构建,以明确区分它与Java
源代码到字节码的编译。
Native Image
生成的可执行文件优点:
Spring Native
是Spring
社区的一个开源框架,可以通过GraalVM
将Spring
应用程序编译成原生镜像。
但是在GitHub
可以看到,该项目已经关闭了:
官方推荐使用Spring Boot 3
+GraalVM
官方构建工具实现原生镜像构建,所以要注意Spring Native
已经没必要再去研究及使用了
首先需要安装GraalVM
、native-image
组件,C++
环境,最好不要在Windows
上使用,可以看到Spring Boot
官网给出Windows
上使用的注意事项,就算按步骤做了~ 也有可能各种报错,所以切勿尝试
选择系统对应的GraalVM
、Native-image
安装包并下载,这里我使用的是Linux Ubuntu
系统。
将文件上传到Linux
服务器:
执行以下命令安装GraalVM
# 解压
tar -zxvf graalvm-ce-java17-linux-amd64-22.3.1.tar.gz
# 添加环境变量
vim /etc/profile
# 添加内容
JAVA_HOME=/root/graalvm-ce-java17-22.3.1/
CLASSPATH=$JAVA_HOME/lib/
PATH=$PATH:$JAVA_HOME/bin
export PATH JAVA_HOME CLASSPATH
# 环境变量生效
source /etc/profile
# 查看
java -version
执行以下命令安装native-image
# 安装
gu -L install native-image-installable-svm-java17-linux-amd64-22.3.1.jar
# 查看
gu list
执行以下命令安装C++
环境:
# Ubuntu 系统
sudo apt-get install build-essential libz-dev zlib1g-dev
准备一个Spring Boot 3.0.3
项目:
添加GraalVM
官方提供的构建插件:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!--原生镜像构建插件-->
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>0.9.20</version>
<extensions>true</extensions>
<executions>
<execution>
<id>build-native</id>
<goals>
<goal>compile-no-fork</goal>
</goals>
<phase>package</phase>
</execution>
<execution>
<id>test-native</id>
<goals>
<goal>test</goal>
</goals>
<phase>test</phase>
</execution>
</executions>
<configuration>
<mainClass>com.pearl.nativeimagedemo.NativeImageDemoApplication</mainClass>
<imageName>native-image-demo</imageName>
<buildArgs>
<buildArg>--verbose</buildArg>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
在Linux
服务器上安装Maven
,将项目代码上传到服务器(内存至少6G,太少会卡住),执行mvn -Pnative -DskipTests native:compile
编译,时间还是蛮久的,虽然这个项目比较空。
编译后在target
下生成可执行文件,直接使用./native-image-demo
就可运行,可以看到启动时间只有几十毫秒:
一、函数式编程1、函数式编程简介函数式编程是种编程方式,它将电脑运算视为函数的计算。函数编程语言最重要的基础是λ演算(lambda calculus),而且λ演算的函数可以接受函数当作输入(参数)和输出(返回值)。和指令式编程相比,函数式编程强调函数的计算比指令的执行重要。和过程化编程相比,函数式编程里函数的计算可随时调用。filter函数自动过滤对象的所有元素,返回true才会存入指定对象;Reduce函数对数组内部的所有元素进行汇总;2、代码实例<!DOCTYPE
一.Tomcat 总体结构1.Server(服务器)是Tomcat构成的顶级构成元素,所有一切均包含在Server中,Server的实现类StandardServer可以包含一个到多个Services。2.次顶级元素Service的实现类为StandardService调用了容器(Container)接口,其实是调用了Servlet Engine(引擎),而且StandardServic...
系统的学过编程的人应该都知道,有一门基础课:《数据结构与算法》,这门课很重要,但是许多人却不怎么重视,导致后来算法学习频频碰壁。我不会给大家系统的讲数据结构,但是我会给大家讲一些很有趣的结构,下来的学习还是得靠大家自己努力啦。 这次讲的是模糊查询。
Mono 2.0 是一个里程碑版本,为Linux下.Net程序开发创造了基本框架。不考虑稳定性和可靠性,从功能上考虑,Mono 2.0的Microsoft 兼容的API有了大幅的改进,ADO.NET、ASP.NET 和 Windows.Forms 三大应用API,使得为Linux平台迁移大量的网站、客户端程序和数据库应用程序成为可能。 作为开发平台,Mono平台的两大致命缺点...
昨天尝试使用Sqoop从PostgreSQL向Hive中迁移数据,过程中遇到两个问题,在此记录一下,以备以后遇到类似问题可以快速找到解决方案。问题1:当PostgreSQL表中有json类型时Sqoop命令报以下错误No Java type for SQL type 1111 forcolumn voting_info解决方案:sqoop命令中添加以下类型映射--...
第1章 引言随着互联网应用的广泛普及,海量数据的存储和访问成为了系统设计的瓶颈问题。对于一个大型的互联网应用,每天几十亿的PV无疑对数据库造成了相当高的负载。对于系统的稳定性和扩展性造成了极大的问题。通过数据切分来提高网站性能,横向扩展数据层已经成为架构研发人员首选的方式。水平切分数据库:可以降低单台机器的负载,同时最大限度的降低了宕机造成的损失;负载均衡策略:可以降低单台机器的访
如何解决WARNING C4996问题在VS编译器中经常会出现warning C4996警告问题,如:warning C4996: 'sprintf': This function or variable may be unsafe. Consider using sprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_WAR...
功能验证 用SV SC V 易语言覆盖率驱动的随机约束激励设计师:架构-分模块写代码验证师:同时做verification plan-testbench,比RTL代码编写复杂几倍
前端js二维码生成/保存/打印
USB(Universal Serial BUS,通用串行总线)协议规定,所有的USB设备都有VID(Vendor ID,供应商识别码)和PID(Product ID,产品识别码)。VID由供应商向USB-IF(Implementers Forum,应用者论坛)申请。每个供应商的VID是唯一的,PID由供应商自行决定。主机通过VID和PID来识别不同设备,根据它们(以及设备的版本号),可以给设备加
Flex Builder 3.0正式版+破解补丁&lt;!-- 底部广告 --&gt;&lt;!--google_ad_client = "pub-6015991121575065";google_alternate_color = "FFFFFF";google_ad_width = 468;google_ad_height = 60;google_ad_format =...
/************************************************************** 函数名称: ADCConfig** 功能描述: ADC配置** 输入参数: NULL** 输出参数: NULL** 返回值: NULL ** 作者: Donny** 日期: 2018.5.21**********************************...