交待一下
本文先用一個簡單的java文件生成了exe,再搭建了maven項目,又把maven項目生成了EXE,整個沒有什么毛病。。。就是坑有點多
遇到問題可以到群4915800 找我
關於原生程序運行時獲取反射信息的問題
這個問題已經完美解決了,請看
https://www.cnblogs.com/cfas/p/16339789.html
graalvm組件介紹
Graal編譯器
GraalVM的核心就是Graal編譯器,一款優秀的JIT編譯器。它即可以當作JIT編譯器使用,也可以用作提前編譯的靜態編譯器。
Graal也是使用Java語言來編寫(JVMCI),常見的編譯器優化它都支持,並且支持更復雜的優化(部分逃逸分析、激進預測優化等),自JDK10開始,HotSpot也引入了Graal。主要作為C2的替代者,相較於C2,它借鑒了C2的優點,擯棄了C2的缺陷,在可維護性和可擴展性上面都優於C2(C2已經復雜到連原作者Cliff都不願意再維護了)。但是Graal目前還處於實驗階段,默認不開啟,需要使用參數來激活。
Truffle
truffle是GraalVM的另一個關鍵組件,它是一款編程語言的實現框架,它提供了一套API,可以用它來實現某一門語言的AST解釋器,並可以被Graal編譯器優化。目前大部分語言的解釋器都已經由Oracle實現了,從上圖可以看到:JS、Ruby、R、Python、Sulong(Sulong是一個針對LLVM IR的解釋器)。也有很多人實現了其它語言的解釋器並放在了Github上。
Truffle的精華在於:所有語言都是使用統一的協議來開發對應的解釋器,也就意味着,運行時所有解釋器都可以通過統一的協議來操作不同編程語言中的對象。所有不語言編寫或混合編寫的代碼,在運行時都是一樣的。都可以像優化正常代碼一樣,來優化不同語言或多語言寫的程序,不會有任何額外的開銷。
這意味着當你使用Java開發時,發現Python有一個非常好用的庫,但是Java沒有。這時你不用再使用Java再實現一遍Pyhton庫,直接在Java代碼中調用Python庫即可。
另一個好處是,因為運行時看來所有的語言都是一樣的,這就意味着工具也可以是多語言的,比如使用VisualVM來分析JavaScript的內存使用等
Substrate VM
源碼 https://github.com/oracle/graal/tree/master/substratevm
SubstrateVM是GraalVM的一個AOT(Ahead-Of-Time)編譯框架。AOT即提前編譯,和即時編譯(JIT)不同,AOT是在程序運行之前,就將字節碼轉換為機器碼,可以直接編譯成可獨立運行的執行文件(本地鏡像)或共享庫。
SubstrateVM 的設計初衷是提供一個高啟動性能、低內存開銷,並且能夠無縫銜接 C 代碼的 Java 運行時。SubtrateVM完全脫離了HotSpot虛擬機,並擁有獨立的運行時,包含異常處理,同步,線程管理,內存管理(垃圾回收)和 JNI 等組件。
從執行時間上來划分,SubstrateVM可以分為兩部分:native image generator 以及 SubstrateVM 運行時:
n### ative image generator
包含了AOT 編譯邏輯。它本身是一個 Java 程序,將使用 Graal 編譯器將 Java 類文件編譯為可執行文件或者動態鏈接庫。
在進行編譯之前,native image generator 將采用指針分析(points-to analysis),從用戶提供的程序入口出發,探索所有可達的代碼。在探索的同時,它還將執行初始化代碼,並在最終生成可執行文件時,將已初始化的堆保存至一個堆快照之中。這樣一來,SubstrateVM 將直接從目標程序開始運行,而無須重復進行 Java 虛擬機的初始化。
SubstrateVM 運行時
一個脫離了HotSpot的精簡運行時,經過 AOT 編譯的目標程序將跑在該運行時之上。
SubstrateVM的啟動時間和內存開銷非常少,而且由於是提前編譯,則程序一啟動便可以獲得良好的性能(性能峰值可能會比JIT更差,但是穩定性會更好)。
但是它目前來看也有很多問題:
很明顯它沒有辦法再做到 “Write once,Run anywhere” 了
Java反射機制是在運行期間動態的調用API,但是具體調用哪些接口,很明顯在程序沒有運行起來時的編譯期是沒辦法知道的。需要開發者明確的告知GaalVM有哪些代碼可能被反射調用(通過JSON配置文件的形式),這就很惡心了,基本上是不可實現的。
本地鏡像由於沒有運行在HotSpot上,那么一切HotSpot虛擬機本身的內部接口(JVMTI、JVMCI等)都不復存在。大量的Agent的調試工具都無法使用,噩夢!
在我們現在的微服務架構中,本地鏡像要比完整的JVM環境+應用要好用的多,性能穩定、啟動時間極快、消耗的資源也更加的少。但是前提是要能夠解決上面的攔路虎。
gu(Graal updater)可以用來安裝 Python,R以及 Ruby的語言包
graalvm相關下載
https://github.com/graalvm/graalvm-ce-builds/releases/tag/vm-20.2.0
- graalvm-ce-java17-windows-amd64-22.0.0.2
- native-image-installable-svm-java17-windows-amd64-22.0.0.2.jar
我是下載了這兩個
關於C++編譯器的環境變量
這個步驟必須成功,否則編譯會失敗。。
不知道gralvm是否支持clang編譯,看網上大部分的文章都是用MSVC的編譯器,都講的是在環境變量中設置MSVC的地址,由於我之前編譯過V8引擎,所以本地的C++環境有點亂,反正我是沒設置成功。
我是直接裝了VS2019。。至於那個版本我回頭發上來
至於裝VS的那些組件的可以看這個文章
https://blog.csdn.net/qq_26212181/article/details/121418452
由於裝了VS2019之后 WIN的菜單欄會有一個專門啟動CL編譯器的CMD工具
注意看,我用的是X64的 不是X64-X86的!
在這個地方找出來
也可以直接到VS目錄下找,注意 不同的VS版本可能路徑不同,大家可以搜索一下
配置graalVm環境變量
GRAALVM_HOME
JAVA_HOME和graalvmHOME一樣 環境變量都一樣
D:\AAAA_WORK\java\graalvm-ce-java17-windows-amd64-22.0.0.2\graalvm-ce-java17-22.0.0.2
## 安裝依賴
執行下面的命令`gu.cmd` 這個玩意兒 網上的屌絲們 都是說gu 實際上要gu.cmd 謝特
gu.cmd -L install native-image-installable-svm-java17-windows-amd64-22.0.0.2.jar
Processing Component archive: native-image-installable-svm-java17-windows-amd64-22.0.0.2.jar
Installing new component: Native Image (org.graalvm.native-image, version 22.0.0.2)
創建編譯測試代碼HelloWorld.java:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World");
}
}
編譯HelloWorld為class
javac HelloWorld.java
進入到VS2019的專用編譯工具(CMD)
輸入 native-image HelloWorld
開始等待吧....
花了4分鍾。。。。
執行生成的EXE
問題
- Get-Unique : 找不到接受實際參數“install ”的位置形式參數。
執行 gu 依賴管理時 出現的問題
大部分網上的文章 直接用的gu 其實在win下需要輸入gu.cmd 坑爹把
命令行需要輸入的是 gu.cmd
查看當前裝了那些依賴
gu.cmd list
maven項目
本來想着用springboot項目生成exe的,但估計配置還有很多問題,所以先用maven項目試試
idea 搭建maven項目
https://www.cnblogs.com/lhboke/p/12858844.html
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<groupId>com.langs</groupId>
<version>1.0-SNAPSHOT</version>
<modelVersion>4.0.0</modelVersion>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<artifactId>demo3</artifactId>
<build>
<finalName>native-image-js</finalName>
<plugins>
<plugin>
<groupId>org.graalvm.nativeimage</groupId>
<artifactId>native-image-maven-plugin</artifactId>
<version>20.3.0</version>
<configuration>
<!-- imageName用於設置生成的二進制文件名稱 -->
<imageName>${project.artifactId}</imageName>
<!-- mainClass用於指定main方法類路徑 -->
<mainClass>com.lang.Application</mainClass>
<!-- native image 編譯參數文檔:https://docs.oracle.com/en/graalvm/enterprise/20/docs/reference-manual/native-image/NativeImageMavenPlugin/ -->
<buildArgs>
--no-fallback
</buildArgs>
</configuration>
<executions>
<execution>
<goals>
<goal>native-image</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
注意maven 項目的目錄
src/main/java 這是必須的約定
否則生成的jar中不會有class 運行jar時,也會報找不到main class
maven項目生成EXE
生成EXE前提是要打好jar包,因為環境配置的問題,直接在IDEA中使用maven插件生成package會報錯。
直接在MS編譯器的控制台上進入項目目錄,執行mvn -Pnative clean package
即可,一次搞定
實際上就是調用的 這樣的指令... graalvm團隊的思路就是maven生成JAR 然后用下面的指令打包
native-image -cp native-image-js.jar Hello
我先按照maven的方式打包(注意 我所有的操作都在X64的VS的命令行工具下執行的)
mvn -Pnative clean package
生成后的EXE
出現問題
Image building on Java 11+ without native-image requires MAVEN_OPTS='--add-exports=java.base/jdk.internal.module=ALL-UNNAMED'
這個問題 總的說來是graalvm的BUG
把graalvm下的svm/bin下的native-image.exe 拷貝到 D:\AAAA_WORK\java\graalvm-ce-java17-windows-amd64-22.0.0.2\graalvm-ce-java17-22.0.0.2\bin 下面
對比下兩個路徑
D:\AAAA_WORK\java\graalvm-ce-java17-windows-amd64-22.0.0.2\graalvm-ce-java17-22.0.0.2\lib\svm\bin
D:\AAAA_WORK\java\graalvm-ce-java17-windows-amd64-22.0.0.2\graalvm-ce-java17-22.0.0.2\bin
這是graalvm對沒有調起cmd那個文件引起的。
Error: Classes that should be initialized at run time got initialized during image building
出現這個情況 表示需要在編譯參數中增加對應的類
class io.netty.util.internal.PlatformDependent0$6 cannot access class jdk.internal.misc.Unsafe (in module java.base) because module java.base does not export jdk.internal.misc to unnamed module
class io.netty.util.internal.PlatformDependent0$6 cannot access class jdk.internal.misc.Unsafe (in module java.base) because module java.base does not export jdk.internal.misc to unnamed module
這種情況是因為JAVA9以上的 三方包 需要在JVM編譯參數中加上需要導入的module 上面就是缺少jdk.internal.misc.Unsafe
--add-exports=java.base/jdk.internal.misc.Unsafe=ALL-UNNAMED
參考
https://www.graalvm.org/docs/getting-started/
https://www.graalvm.org/reference-manual/native-image/
JAVAFX生成EXE 其中講到了關於反射類和DLL的一些處理細節,值得一看
https://www.cnblogs.com/javalinux/p/15791024.html