Spring Boot 3.0系列【2】部署篇之使用GraalVM构建原生镜像_graalvm maven_云烟成雨TD的博客-程序员秘密

技术标签: 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虚拟机进行动态编译,因此启动速度很快、内存消耗也很低。

JIT & AOT

在正式介绍Native Image之前,我们需要了解下JIT AOT的相关概念。

通常程序有两种运行方式:

  • 动态解释:解释执行,运行时翻译为机器码。
  • 静态编译:程序在执行前全部被翻译为机器码,可以直接运行二进制文件。

JIT (动态编译)

JITJust-in-time的缩写,一般称为即时编译动态编译Java源代码在运行的过程中,类加载器将需要运行的字节码处理并分配到内存中,然后JVM执行引擎需要调用解释器将字节码翻译为计算机能执行的机器码,最后执行机器指令。

需要解释执行势必会造成运行效率降低,为了提高执行速度,JAVA引入了 JIT 编译器。当某个方法或代码块运行特别频繁的时候,JVM会将其标注为热点代码, JIT 编译器会将热点代码编译成本地机器相关的机器码,优化后进行缓存,下次执行这些代码时,直接调用缓存中的机器码,无需重复解释,在一定程度上提高了执行速度。
在这里插入图片描述

AOT(静态编译)

JAVA一直在努力提高启动和运行时性能,希望其能够在更广泛的场景达到或接近本地语言的性能。虽然引入了JIT 编译器,但是需要花费较长时间才能热身完,而且有些Java方法还没法编译,性能方面也会下降。

AOTAhead-of-Time的缩写,一般称为静态编译。程序在执行前全部被翻译为机器码,可以直接运行二进制文件,比如C++就是使用静态编译。
在这里插入图片描述

JDK 9 中, AOT作为实验特性被引入,只支持 java.base 模块可以编译成AOT库,使用jaotc工具将Java类文件编译为本机代码,避免了 JIT 预热等各方面的开销。但是实际运行效果不尽人意,最终在JDK 16中被删除。

JDK 17中,也移除了实验性的AOTJIT,彻底拥抱GraalVM实现静态编译。

在当前微服务、云原生盛行的时代,JAVA 程序显得越来越臃肿,虽然使用AOT也有诸多缺点,比如打包时间长、舍弃平台无关性、反射、JNI、动态代理的分析能力有限。但是JAVA 必定会向AOT发展,否则在云原生时代,可以能被其他后起之秀慢慢蚕食市场。

GraalVM

简介

官网地址
GitHub地址

GraalVM是一个高性能跨语言虚拟机,其目的是提升Java和其他JVM语言编写程序的执行速度,同时也为JavaScriptPython和许多其他流行语言提供运行时环境。起始于 2011 年 Oracle 实验室的一个研究项目。
在这里插入图片描述GraalVM三大核心:

  • Java 虚拟机提供高性能的JIT编译器
  • 高性能的AOT编译器,提前将 Java 字节码编译为本机机器码。
  • 多种语言的支持,GraalVMTruffle语言实施框架可与 GraalVM 编译器协作,以卓越性能运行 JavaScript、Python、Ruby 以及 JVM 支持的其他语言。

GraalVM提供了两种运行Java应用程序的方法:

  • HotSpot JVM上使用实时JIT编译器
  • 使用AOTJava应用程序编译的本地可执行文件

社区版和企业版的一些区别

  • 社区版基于Open JDK,由社区组织维护
  • 社区版无定制的高级编译器优化
  • 在原生镜像中,社区版无压缩指针、配置文件引导优化、G1垃圾收集

在这里插入图片描述

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可以在AMD6AArch64等服务器平台生成环境使用。
在这里插入图片描述

3、Java on Truffle

Java on Truffle 是一个 JVM 实现,它使用了 Truffle 多语言执行框架。提供了Java虚拟机所有的核心组件,实现了与Java运行时环境库相同的API,并重用GraalVM中的所有JAR和本机库。支持多语言互操作,例如,JavaScript 程序可以运行Ruby方法,无需制作副本就能共享数值。基于JVM运行时,Truffle 能够与GraalVM编译器协作,将受支持语言编译为本机机器码,从而优化性能。

Native Image(原生镜像)

了解了GraalVM之后,我们着重了解并使用Native Image技术将一个Spring Boot 3项目编译为一个可执行二进制文件。

Native Image:是一种将Java代码提前编译为二进制文件的技术,即本机可执行文件。本机可执行文件只包含运行时所需的代码,即应用程序类、标准库类、语言运行时以及来自JDK的静态链接本机代码

Native Image处理应用程序类和其他元数据,以创建特定操作系统和体系结构的二进制文件。首先,本地镜像工具对代码执行静态分析,以确定应用程序运行时可访问的类和方法。其次,它将类、方法和资源编译成二进制文件。整个过程被称为构建,以明确区分它与Java源代码到字节码的编译。
在这里插入图片描述

Native Image生成的可执行文件优点:

  • 使用虚拟机所需资源的一小部分,运行时更低的内存消耗
  • 以毫秒为单位启动
  • 立即提供最高性能,无需预热
  • 可以打包成轻量级容器映像,以实现快速高效的部署
  • 更不容易遭到破解、攻击

Spring Native

Spring NativeSpring 社区的一个开源框架,可以通过GraalVMSpring应用程序编译成原生镜像。

但是在GitHub可以看到,该项目已经关闭了:
在这里插入图片描述
官方推荐使用Spring Boot 3+GraalVM官方构建工具实现原生镜像构建,所以要注意Spring Native已经没必要再去研究及使用了

案例演示

官方环境要求文档

首先需要安装GraalVMnative-image组件,C++环境,最好不要在Windows 上使用,可以看到Spring Boot官网给出Windows 上使用的注意事项,就算按步骤做了~ 也有可能各种报错,所以切勿尝试
在这里插入图片描述

1. 安装 GraalVM Native-image C++环境

下载地址

选择系统对应的GraalVMNative-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

2. 项目测试

准备一个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就可运行,可以看到启动时间只有几十毫秒:
在这里插入图片描述

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_43437874/article/details/129085457

智能推荐

Vue基础知识总结 4:vue组件化开发_哪 吒的博客-程序员秘密

一、函数式编程1、函数式编程简介函数式编程是种编程方式,它将电脑运算视为函数的计算。函数编程语言最重要的基础是λ演算(lambda calculus),而且λ演算的函数可以接受函数当作输入(参数)和输出(返回值)。和指令式编程相比,函数式编程强调函数的计算比指令的执行重要。和过程化编程相比,函数式编程里函数的计算可随时调用。filter函数自动过滤对象的所有元素,返回true才会存入指定对象;Reduce函数对数组内部的所有元素进行汇总;2、代码实例&lt;!DOCTYPE

深入理解Tomcat容器的结构_tomcat容器的理解_loulanyue_的博客-程序员秘密

一.Tomcat 总体结构1.Server(服务器)是Tomcat构成的顶级构成元素,所有一切均包含在Server中,Server的实现类StandardServer可以包含一个到多个Services。2.次顶级元素Service的实现类为StandardService调用了容器(Container)接口,其实是调用了Servlet Engine(引擎),而且StandardServic...

结构的艺术:模糊查询_模糊查询算法_算法吹的博客-程序员秘密

系统的学过编程的人应该都知道,有一门基础课:《数据结构与算法》,这门课很重要,但是许多人却不怎么重视,导致后来算法学习频频碰壁。我不会给大家系统的讲数据结构,但是我会给大家讲一些很有趣的结构,下来的学习还是得靠大家自己努力啦。 这次讲的是模糊查询。

艾伟:聊一聊MONO的前前后后、里里外外_weixin_34209406的博客-程序员秘密

      Mono 2.0 是一个里程碑版本,为Linux下.Net程序开发创造了基本框架。不考虑稳定性和可靠性,从功能上考虑,Mono 2.0的Microsoft 兼容的API有了大幅的改进,ADO.NET、ASP.NET 和 Windows.Forms 三大应用API,使得为Linux平台迁移大量的网站、客户端程序和数据库应用程序成为可能。     作为开发平台,Mono平台的两大致命缺点...

Sqoop从PostgreSQL导入Hive遇到的两个问题_error orm.classwriter: cannot resolve sql type 111_post_yuan的博客-程序员秘密

昨天尝试使用Sqoop从PostgreSQL向Hive中迁移数据,过程中遇到两个问题,在此记录一下,以备以后遇到类似问题可以快速找到解决方案。问题1:当PostgreSQL表中有json类型时Sqoop命令报以下错误No Java type for SQL type 1111 forcolumn voting_info解决方案:sqoop命令中添加以下类型映射--...

数据库分库,分表,主从,读写分离,集群,负载均衡_xdtlsfsfykdf的博客-程序员秘密

第1章 引言随着互联网应用的广泛普及,海量数据的存储和访问成为了系统设计的瓶颈问题。对于一个大型的互联网应用,每天几十亿的PV无疑对数据库造成了相当高的负载。对于系统的稳定性和扩展性造成了极大的问题。通过数据切分来提高网站性能,横向扩展数据层已经成为架构研发人员首选的方式。水平切分数据库:可以降低单台机器的负载,同时最大限度的降低了宕机造成的损失;负载均衡策略:可以降低单台机器的访

随便推点

如何解决WARNING C4996问题_warningc4996_不负韶华-z的博客-程序员秘密

如何解决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...

1 Device Under Test(DUT)_luoai_2666的博客-程序员秘密

功能验证 用SV SC V 易语言覆盖率驱动的随机约束激励设计师:架构-分模块写代码验证师:同时做verification plan-testbench,比RTL代码编写复杂几倍

USB设备的VID与PID_bobuddy的博客-程序员秘密

USB(Universal Serial BUS,通用串行总线)协议规定,所有的USB设备都有VID(Vendor ID,供应商识别码)和PID(Product ID,产品识别码)。VID由供应商向USB-IF(Implementers Forum,应用者论坛)申请。每个供应商的VID是唯一的,PID由供应商自行决定。主机通过VID和PID来识别不同设备,根据它们(以及设备的版本号),可以给设备加

【转自YoungBoy】FLEX BUILDER3正式版 AIR正式版破解补丁_iteye_3224的博客-程序员秘密

Flex Builder 3.0正式版+破解补丁&amp;lt;!-- 底部广告 --&amp;gt;&amp;lt;!--google_ad_client = &quot;pub-6015991121575065&quot;;google_alternate_color = &quot;FFFFFF&quot;;google_ad_width = 468;google_ad_height = 60;google_ad_format =...

EFM32 ADC使用例程_weixin_30375427的博客-程序员秘密

/************************************************************** 函数名称: ADCConfig** 功能描述: ADC配置** 输入参数: NULL** 输出参数: NULL** 返回值: NULL ** 作者: Donny** 日期: 2018.5.21**********************************...

推荐文章

热门文章

相关标签