文章目录
GraalVM 可以使得 Java 应用可以脱离 JVM 来运行。换句话说,它可以使 Java 应用在没有 Java 的环境中运行。不过目前,这项技术还有很多的问题。由于 GraalVM 使用的是 AOT 技术,所以它不能直接用于打包含 Java 反射、动态代理的 Java 项目。一般来说,如果想对含 Java 反射、动态代理的 Java 项目使用 GraalVM 进行打包,必须提前向 GraalVM 提供一个列出了涉及 Java 反射、动态代理所有的类的 JSON 列表,否则,要么打包时会失败,要么打包后的应用在运行时会失败。
另外,GraalVM 打包对内存的较高。在打包过程对内存的占用超 6 G 以上、CPU 占用 99% 是很常见的事情。此外,GraalVM 打包所用的时间也较长,可长达几分钟。正因为如此,在打包过程中,有些电脑管家类软件还会报毒。
JavaFX 支持使用 FXML 这种文本语言来创建组件,然而对于这种文本语言,Java 往往都中借助于反射技术来实现与其兼容。FXML 也不例外。之前,笔者已经编写过使用 GraalVM 将基本的 Java 项目打包成 EXE 的教程,可见笔者的另一篇博客:
使用 GraalVM 将基本的 Java 项目打包成 EXE:
https://blog.csdn.net/wangpaiblog/article/details/122422987
这次,笔者来示范一下如何使用 GraalVM 将纯 JavaFX 项目打包成 EXE。纯 JavaFX 项目指的是此 Java 项目没有使用其它涉及反射、动态代理的 Java 框架或技术。
运行环境:
GraalVM Enterprise 21.3.0
Java 语言级别:17
JavaFX 17.0.1
Maven 3.8.3
IntelliJ IDEA 2021.2.2 (Ultimate Edition)
Windows 10 教育版 64位
GraalVM 的环境搭建
-
GraalVM 有两种版本,Community 和 Enterprise。其中,前者对应于 OpenJDK,后者对应 Oracle JDK。需要根据自己的需要进行选择,本文以
GraalVM Enterprise 21.3.0
为例。GraalVM 版本选择网址:https://www.graalvm.org/downloads/
-
下载
GraalVM Enterprise 21.3.0
需要进入 Oracle 官网,网址:https://www.oracle.com/downloads/graalvm-downloads.html?selected_tab=1 -
先下载
Oracle GraalVM Enterprise Edition Core
。下载完成之后,应该会得到一个 zip 压缩包。 -
再在刚才的页面下载
Oracle GraalVM Enterprise Edition Native Image
。这次得到的应该是一个 JAR 包。 -
将前面的 zip 压缩包置入自己喜欢的文件夹下解压,解压得到的就是 GraalVM 程序文件。与安装 JDK 时类似,将含 bin 的目录作为 GraalVM 的安装目录。设环境变量如下:
-
变量名:ORACLE_GRAALVM_HOME
变量值:C:\Program Files\Java\graalvm-ee-java17-21.3.0
-
变量名:GRAALVM_HOME
变量值:%ORACLE_GRAALVM_HOME%
-
变量名:Path
变量值:%GRAALVM_HOME%\bin
-
-
如果上面的环境变量设置成功,在 CMD 的任意路径中输入以下命令应该能看到上面设置的路径。
where java
where gu
C:\>where java C:\Program Files\Java\graalvm-ee-java17-21.3.0\bin\java.exe C:\>where gu C:\Program Files\Java\graalvm-ee-java17-21.3.0\bin\gu.cmd
-
安装
Native Image
包。Native Image
包就是前面下载的 JAR 包。输入以下命令对Native Image
进行本地安装。gu -L install Native Image 的 JAR 包路径
其中,
Native Image 的 JAR 包路径
要替换成实际的路径。 -
GraalVM 的运行需要 Visual Studio 中的 MSVS 的支持,因此需要下载 Visual Studio。编写本博客时,笔者下载的是
Microsoft Visual Studio Enterprise 2022 (64 位)
。Visual Studio 下载网址:https://visualstudio.microsoft.com/zh-hans/vs/
-
现在,Visual Studio 在官网上只会提供在线安装包。下载完在线安装包后,选择安装含 MSVS 的选项。
-
至此,GraalVM 的运行环境已经搭成。下面将开始使用 GraalVM 进行打包。
打包不含 FXML 的纯 JavaFX 项目
-
为此,笔者编写了一个简易的不含 FXML 的纯 JavaFX 项目。
【踩坑提醒】
-
对于 Java 8 以上的 JavaFX 项目,必须使用 Java 模块系统才能对其进行 GraalVM 打包。出于本文的重点,这里不详细介绍什么是 Java 模块系统以及它的语法。通常,构建 Java 模块系统只需要在顶级目录中添加一个模块声明文件
module-info.java
。 -
项目的整个文件路径中都不能含有中文,否则后面的 GraalVM 打包将失败。
-
-
为了方便读者自行快速实践,项目源码已上传至 GitHub,位于 Git 分支
withoutfxml
中,可免费下载,链接:https://github.com/wangpaiblog/20220209-purejavafx -
这个项目所做的事情很简单,模拟简单的组件间的消息传递。如图所示:
-
核心代码如下:
package org.wangpai.demo.purejavafx; import java.io.IOException; import javafx.application.Application; import javafx.geometry.Insets; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.TextArea; import javafx.scene.layout.VBox; import javafx.stage.Stage; import static javafx.geometry.Pos.BOTTOM_RIGHT; import static javafx.geometry.Pos.CENTER; public class App extends Application { public static final long START_TIME = System.currentTimeMillis(); @Override public void start(Stage stage) throws IOException { var output = new TextArea(); output.setPrefSize(200, 200); output.setEditable(false); output.setWrapText(true); var input = new TextArea(); input.setPrefSize(200, 200); input.setWrapText(true); var send = new Button("发送(S)"); send.setOnAction(event -> { var msg = input.getText(); System.out.println(msg); output.setText(msg); input.requestFocus(); input.clear(); }); var sendVbox = new VBox(send); sendVbox.setPrefSize(200, 0); sendVbox.setAlignment(BOTTOM_RIGHT); var vbox = new VBox(20, output, input, sendVbox); vbox.getStylesheets().add(App.class.getResource("/org/wangpai/demo/purejavafx/MainFace.css").toExternalForm()); vbox.setAlignment(CENTER); vbox.setPrefSize(500, 500); vbox.setPadding(new Insets(20)); Scene scene = new Scene(vbox, 500, 500); stage.setTitle("javafx without fxml"); stage.setScene(scene); stage.show(); output.setText("应用启动用时:" + (System.currentTimeMillis() - START_TIME) / 1000.0 + "s"); input.requestFocus(); } public static void main(String[] args) { launch(); } }
-
因为本项目是使用 Maven 进行构建的,所以这里也使用 Maven 插件来进行 GraalVM 打包,这需要在 Maven 的 POM 文件中进行配置。关于 Maven 的安装与使用,笔者在此之前也编写了一些博客,故此处不再对 Maven 做过多介绍。
-
这个插件是 Gluon 提供的,Gluon 现在实质上已经成为了 JavaFX 技术的主要提供商。
Gluon 官网:https://gluonhq.com/
JavaFX 的 GraalVM 打包 Maven 插件官方文档网址:https://docs.gluonhq.com/#_gluonfx_plugin_for_maven
-
POM 的插件配置代码模板如下:
<project...> <build> <plugins> <plugin> <groupId>com.gluonhq</groupId> <artifactId>gluonfx-maven-plugin</artifactId> <version>1.0.10</version> <configuration> <target>host</target> <mainClass>入口类的包名.类名</mainClass> </configuration> </plugin> </plugins> </build> </project>
注意:对于纯 JavaFX 的 Java 模块化项目,其入口类应该是一个继承了 Application 的类。
-
如果需要在 JavaFX 中使用一些特殊的资源文件,这需要在此列出所有的这些文件。此插件打包时已经内置了如下类型的文件。
-
png
,jpg
,jpeg
,gif
,bmp
,ttf
,raw
-
xml
,fxml
,css
,gls
,json
,dat
, -
license
,frag
,vert
,obj
已经内置了类型的文件不需要自行列出(即上面列出的类型的文件不属于上面所言的
特殊的资源文件
)。更多的信息,可见:https://docs.gluonhq.com/#_resourceslist
或
-
-
列出的方法是,在
<configuration/...>
中使用<bundlesList/...>
、<list/...>
,然后在<list/...>
中依次列出。如:<project...> <build> <plugins> <plugin> <groupId>com.gluonhq</groupId> <artifactId>gluonfx-maven-plugin</artifactId> <version>1.0.10</version> <configuration> <target>host</target> <mainClass>入口类的包名.类名</mainClass> <bundlesList> <list>文件路径.文件名</list> </bundlesList> <reflectionList> </configuration> </plugin> </plugins> </build> </project>
不过要注意,这个列出文件的时候,要像 Java 类一样,使用点号来代替路径分隔符,且不需要加上文件后缀。如:
<reflectionList> <list>org.wangpai.demo.purejavafx.MainFace</list> </reflectionList>
-
在项目目录下的命令行中输入如下命令使用 MSVS 的环境。这个命令是用于设置临时设置与 MSVS 相关的环境变量,因此每次总是要输。
vcvars64.bat 的路径
"
-
vcvars64.bat
提供了 MSVS 的运行环境。虽然也可以通过设置 MSVS 的环境变量来代替,不过这样要设置的环境变量会有很多。对于笔者的Microsoft Visual Studio Enterprise 2022 (64 位)
,以上的命令为:call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
【踩坑提醒】
需要进入 CMD 终端才能运行此命令。 IntelliJ IDEA 的终端默认使用 PowerShell 来运行命令,如果使用 IntelliJ IDEA 终端,需要将 IntelliJ IDEA 的终端设置成 CMD 模式(Command Prompt):
-
在项目目录下的命令行中输入如下命令进行 GraalVM 打包:(再次提醒:项目的整个文件路径中都不能含有中文,否则 GraalVM 打包将失败。)
mvn clean gluonfx:build
-
打包完成之后,应该可以看到在项目路径
\target\gluonfx\x86_64-windows\
下的 EXE 文件。对于运行来说,就只有生成的 EXE 文件是核心文件,其它文件只是打包过程中生成的中间产物,都可以删除。 -
打包完成之后,可以直接在操作系统或命令行中运行此 EXE 文件,也可以使用如下命令:
mvn gluonfx:nativerun
如果选择直接在操作系统或命令行中运行此 EXE 文件,则标准输出流将不会定向到运行来源,这意味着将无法看到
System.out.println(...)
这之类代码的输出。
打包含 FXML 的纯 JavaFX 项目
-
为此,笔者对上面的 JavaFX 项目作对应改编。改编之后的功能与改编之前完全一样,这是为了方便之后的测评。
【踩坑提醒】
-
对于 Java 8 以上的 JavaFX 项目,必须使用 Java 模块系统才能对其进行 GraalVM 打包。出于本文的重点,这里不详细介绍什么是 Java 模块系统以及它的语法。通常,构建 Java 模块系统只需要在顶级目录中添加一个模块声明文件
module-info.java
。 -
项目的整个文件路径中都不能含有中文,否则后面的 GraalVM 打包将失败。
-
-
为了方便读者自行快速实践,项目源码已上传至 GitHub,位于 Git 分支
withfxml
中,可免费下载,链接:https://github.com/wangpaiblog/20220209-purejavafx -
这个项目所做的事情很简单,模拟简单的组件间的消息传递。这和前面是一样的,故此处不再赘述。
-
核心代码如下:
package org.wangpai.demo.purejavafx; import java.io.IOException; import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Scene; import javafx.stage.Stage; public class App extends Application { public static final long START_TIME = System.currentTimeMillis(); @Override public void start(Stage stage) throws IOException { FXMLLoader fxmlLoader = new FXMLLoader(App.class.getResource("MainFace.fxml")); Scene scene = new Scene(fxmlLoader.load(), 500, 500); stage.setTitle("javafx with fxml"); stage.setScene(scene); stage.show(); MainFace.afterUiShow("应用启动用时:" + (System.currentTimeMillis() - START_TIME) / 1000.0 + "s"); } public static void main(String[] args) { launch(); } }
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.geometry.Insets?> <?import javafx.scene.control.Button?> <?import javafx.scene.control.TextArea?> <?import javafx.scene.layout.VBox?> <VBox alignment="CENTER" spacing="20.0" xmlns:fx="http://javafx.com/fxml" prefHeight="500" prefWidth="500" fx:controller="org.wangpai.demo.purejavafx.MainFace" stylesheets="@MainFace.css"> <padding> <Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/> </padding> <TextArea prefHeight="200" prefWidth="200" editable="false" wrapText="true" fx:id="output"/> <TextArea prefHeight="200" prefWidth="200" wrapText="true" fx:id="input"/> <VBox alignment="BOTTOM_RIGHT" prefHeight="0" prefWidth="200"> <Button text="发送(S)" onAction="#onActionSend"/> </VBox> </VBox>
package org.wangpai.demo.purejavafx; import java.net.URL; import java.util.ResourceBundle; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.TextArea; public class MainFace implements Initializable { private static MainFace mainFace; @FXML private TextArea output; @FXML private TextArea input; @FXML public void onActionSend(ActionEvent event) { var msg = this.input.getText(); System.out.println(msg); this.output.setText(msg); this.input.requestFocus(); this.input.clear(); } @Override public void initialize(URL url, ResourceBundle resourceBundle) { mainFace = this; } public static void afterUiShow(String msg) { mainFace.input.requestFocus(); mainFace.output.setText(msg); } }
-
因为本项目是使用 Maven 进行构建的,所以这里也使用 Maven 插件来进行 GraalVM 打包,这需要在 Maven 的 POM 文件中进行配置。关于 Maven 的安装与插件,笔者在此之前也编写一些博客,故此处不再对 Maven 做过多介绍。
-
这个插件是 Gluon 提供的,Gluon 现在实质上已经成为了 JavaFX 技术的主要提供商。
Gluon 官网:https://gluonhq.com/
JavaFX 的 GraalVM 打包 Maven 插件官方文档网址:https://docs.gluonhq.com/#_gluonfx_plugin_for_maven
-
POM 的插件配置代码模板如下:
<project...> <build> <plugins> <plugin> <groupId>com.gluonhq</groupId> <artifactId>gluonfx-maven-plugin</artifactId> <version>1.0.10</version> <configuration> <target>host</target> <mainClass>入口类的包名.类名</mainClass> <reflectionList> <list>JavaFX 控制器类的包名.类名</list> </reflectionList> </configuration> </plugin> </plugins> </build> </project>
注意:
-
对于纯 JavaFX 的 Java 模块化项目,其入口类应该是一个继承了 Application 的类。
-
前面提到过,FXML 会导致 JavaFX 使用反射,为了让 GraalVM 打包成功,这需要全部列出所有的 JavaFX 控制器类。
-
-
如果需要在 JavaFX 中使用一些特殊的资源文件,这需要在此列出所有的这些文件。此插件打包时已经内置了如下类型的文件。
-
png
,jpg
,jpeg
,gif
,bmp
,ttf
,raw
-
xml
,fxml
,css
,gls
,json
,dat
, -
license
,frag
,vert
,obj
已经内置了类型的文件不需要自行列出(即上面列出的类型的文件不属于上面所言的
特殊的资源文件
)。更多的信息,可见:https://docs.gluonhq.com/#_resourceslist
或
-
-
列出的方法是,在
<configuration/...>
中使用<bundlesList/...>
、<list/...>
,然后在<list/...>
中依次列出。如:<project...> <build> <plugins> <plugin> <groupId>com.gluonhq</groupId> <artifactId>gluonfx-maven-plugin</artifactId> <version>1.0.10</version> <configuration> <target>host</target> <mainClass>入口类的包名.类名</mainClass> <bundlesList> <list>文件路径.文件名</list> </bundlesList> <reflectionList> </configuration> </plugin> </plugins> </build> </project>
不过要注意,这个列出文件的时候,要像 Java 类一样,使用点号来代替路径分隔符,且不需要加上文件后缀。如:
<reflectionList> <list>org.wangpai.demo.purejavafx.MainFace</list> </reflectionList>
-
在项目目录下的命令行中输入如下命令使用 MSVS 的环境。这个命令是用于设置临时设置与 MSVS 相关的环境变量,因此每次总是要输。
vcvars64.bat 的路径
"
-
vcvars64.bat
提供了 MSVS 的运行环境。虽然也可以通过设置 MSVS 的环境变量来代替,不过这样要设置的环境变量会有很多。对于笔者的Microsoft Visual Studio Enterprise 2022 (64 位)
,以上的命令为:call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
【踩坑提醒】
需要进入 CMD 终端才能运行此命令。 IntelliJ IDEA 的终端默认使用 PowerShell 来运行命令,如果使用 IntelliJ IDEA 终端,需要将 IntelliJ IDEA 的终端设置成 CMD 模式(Command Prompt):
-
在项目目录下的命令行中输入如下命令进行 GraalVM 打包:(再次提醒:项目的整个文件路径中都不能含有中文,否则 GraalVM 打包将失败。)
mvn clean gluonfx:build
-
打包完成之后,应该可以看到在项目路径
\target\gluonfx\x86_64-windows\
下的 EXE 文件。对于运行来说,就只有生成的 EXE 文件是核心文件,其它文件只是打包过程中生成的中间产物,都可以删除。 -
打包完成之后,可以直接在操作系统或命令行中运行此 EXE 文件,也可以使用如下命令:
mvn gluonfx:nativerun
如果选择直接在操作系统或命令行中运行此 EXE 文件,则标准输出流将不会定向到运行来源,这意味着将无法看到
System.out.println(...)
这之类代码的输出。
测评
前面已经介绍了 GraalVM 打包 JavaFX 项目的方法,不过 GraalVM 是否真的可以提高性能呢?如果可以,提高了多少呢?另外,JavaFX 对 FXML 使用了反射,这会不会大幅度影响性能呢?这种事情还是需要亲身实践一下才行。笔者在前面已经留了一手,在前面已经设置了同条件下无 FXML 与有 FXML 的对照组,用应用标题作区分,并在其中预留了应用启动时间统计。现在来看一下在 JavaFX 中使用和不使用 FXML 的启动时间对比,以及使用 GraalVM 打包前后的启动时间对比。因不同电脑的性能等差异,此结果仅供参考。
在 IntelliJ IDEA 中直接运行
经过多次测试,本示例项目在 IntelliJ IDEA 中直接运行的启动时间结果为:
-
无 FXML 版本:
-
启动时间:约 0.7306 s
-
内存占用:120-130 MB 左右
-
-
有 FXML 版本:
-
启动时间:约 0.7666 s
-
内存占用:120-130 MB 左右
-
可以看出,在 IntelliJ IDEA 环境中,相对于有 FXML 版本,无 FXML 版本的启动时间略小,减少了大约 5% 的启动时间,这个减少的时间不是太明显。另外,内存占用方面,无 FXML 版本也没有明显的优势。
生成 JAR 包后运行
为了模拟真实环境,这里还将每个项目打成 JAR 包作对比测试。打 JAR 包的插件使用的是 maven-shade-plugin
,这个插件的具体配置,已在上面的源代码中给出。关于这个插件的介绍与使用,可见笔者的另一篇博客:
将 Maven 中的多模块项目只打成一个 JAR 包:
https://blog.csdn.net/wangpaiblog/article/details/119628194
-
Oracle JDK 17 体积:288 MB
-
GraalVM Enterprise 21.3.0 体积:861 MB
-
无 FXML 版本:
-
打包后应用体积:8.84 MB
-
启动时间:约 0.8662 s
-
内存占用:90-110 MB 左右
-
-
有 FXML 版本:
-
打包后应用体积:8.84 MB
-
启动时间:约 0.9418 s
-
内存占用:90-110 MB 左右
-
可以看出,在生成 JAR 包后运行,相对于有 FXML 版本,无 FXML 版本的启动时间较小,减少了大约 8% 的启动时间。但内存占用方面,无 FXML 版本没有明显的优势。同时对比 IntelliJ IDEA 环境下的启动时间,可以发现,IntelliJ IDEA 对 Java 项目的启动进行了一些优化,同比减少了约 20% 的启动时间,但代价是占用更多的内存,大约多占用了 15% 的内存。
GraalVM 打包后运行
-
无 FXML 版本:
-
打包耗时:约 2.1 min
-
打包过程中 CPU 最高平均占用:约 99.7%
-
打包过程中内存最高平均占用:约 7.3 G
-
打包后应用体积:69.9 MB
-
启动时间:约 0.3076 s
-
内存占用: 75-85 MB 左右
-
-
有 FXML 版本:
-
打包耗时:约 2.03min
-
打包过程中 CPU 最高平均占用:约 99.4%
-
打包过程中内存最高平均占用:约 7.4 G
-
打包后应用体积:69.7 MB
-
启动时间:约 0.3078 s
-
内存占用:75-85 MB 左右
-
可以看出,GraalVM 打包过程中的消耗是比较大的,但有 FXML 版本与无 FXML 版本对 GraalVM 打包的影响几乎为 0。无论是打包过程中,以及生成的 EXE 的运行,有 FXML 版本与无 FXML 版本之间都没有明显的数据上的差异。这说明,GraalVM 消除了 Java 反射的负面效果。这正是 AOT 技术的体现之一。
对于启动时间,使用 GraalVM 打包后减少了 60% 至 70%,这个减少的时间很可观。对于内存占用,减少了 20% 左右的空间,但是占用的内存依然较大。对于应用体积,相对于原整个 JDK 来说,小了 75%,不过依然较大。总而言之,相对原始的 JAR 包运行,使用 GraalVM 打包后都有一定程度甚至很大幅度上的提高,但相对于 C 家族的 GUI 应用来说,即便是使用 GraalVM 打包,其应用体积、内存占用都没有太大的竞争力。
GraalVM 与 Java 中其它打包成 EXE 的方法对比
但是相对于 Java 中其它打包成 EXE 的方法,如果 GraalVM 打包成功,GraalVM 可以说是最优的方式。 Java 中其它打包成 EXE 的方式无非就如下几种,或者是它们的变体。
-
方式 1:先生成 JAR,然后将运行 JAR 包的命令写在一个脚本中,以后直接运行脚本即可。
但此方法本质上是先运行 Java 虚拟机,然后再用 Java 虚拟机加载运行 Java 代码。Java 虚拟机会占用较高的内存,且在这种方式下,Java 项目无法拥有自己的父进程名,对外只显示为 Java 虚拟机在运行。
-
方式 2:底层原理同方式 1,但将上面的脚本封装成了一个 EXE。
这样做能让普通用户不能直接操作底层的脚本命令,提高了一定的安全系数和用户的友好度。除此之外,它跟方式 1 没有太大的区别。但根据使用的封装软件不同,此 Java 项目可能拥有自己的进程名与图标。
-
方式 3:底层原理同方式 2,但使用了一种 EXE 压缩软件将上面的 JAR 包与脚本封装成了一个 EXE。
-
方式 4:底层原理同方式 3,但这种方式将 JRE 的相对路径信息也储存到了 EXE 中,这样可以直接在此 EXE 附近放一个 JRE 文件,以后就可以在新电脑上无需先安装 Java 即可马上运行。
但这种方式需要向用户提供 JRE,JRE 的体积一般都非常庞大。另外,这种方式本质上与前面几种方式一样,也是先运行 Java 虚拟机,然后再用 Java 虚拟机加载运行 Java 代码。而 Java 虚拟机会占用较高的内存。
-
方式 5:前期步骤同方式 4,但额外借助了 Java 的一些内置工具命令精简了 JRE 体积,使得打包之后应用体积大大减少。
但这种方式需要每次用人力去排除和筛选 JRE,然后编写命令执行,无一般的规律可寻,且误操作之后可能导致打包后运行失败,这会增加人力时间成本。另外,这种方式只是减少了打包之后应用体积,而没有减少运行内存。
-
方式 6:这种方式不同前面的这几种方式。它直接使用 Java 内置的打包命令即可生成 EXE,而无需借助第三方制 EXE 的软件。制好后可在新电脑上无需先安装 Java 即可马上运行。
但这种方式需要每次用人力进行文件布局和多次编写命令执行。另外,这种方式打包后的应用,其运行时本质上还是依赖于 Java 虚拟机。
-
方式 7:底层原理同方式 6,但它被嵌入到 Java 的一种构建工具(如 Maven)的插件中,一定程度可以减少手动配置并适合离不开构建工具的项目。除此之外,它跟方式 6 没有太大的区别。
-
GraalVM 方式:这种方式不同前面的这几种方式。它生成的 EXE 的运行无需借助 Java 虚拟机,更无需先安装 Java,且无论是应用体积还是运行内存在所有的打包方式是都是最少的。
但它不能直接支持含 Java 反射、动态代理(如 CGLIB)的 Java 项目,需要每次用人力统计所有使用过 Java 反射、动态代理的类,无一般的规律可寻。另外,在所有的打包方式中,GraalVM 打包的耗时最长,CPU、内存消耗都是最大的。
总结
本文介绍了使用 GraalVM 成功将纯 JavaFX 项目打包的方法,但 GraalVM 还有很长的一段路要走。对于很多 API,GraalVM 的支持都有限。如果在 Java 中使用了这些 API,GraalVM 的打包都会失败,这无疑是给 Java 开发造成了很多限制。另外,GraalVM 对主流 Java 框架的支持很有限。在项目中使用的 Java 框架越多,GraalVM 的打包流程越复杂,需要注意的东西越多,打包成功率越低。