亚欧色一区w666天堂,色情一区二区三区免费看,少妇特黄A片一区二区三区,亚洲人成网站999久久久综合,国产av熟女一区二区三区

  • 發布文章
  • 消息中心
點贊
收藏
評論
分享

通過 GraalVM 將 Java 程序編譯成本地機器碼!

2023-12-12 02:09:33
71
0

前言

2018年(nian)4月(yue),Oracle Labs新公(gong)開了(le)一(yi)項黑科技:Graal VM。

這是一個在HotSpot虛(xu)擬機(ji)基礎上增強(qiang)而成的跨語言全(quan)棧虛(xu)擬機(ji),可以作(zuo)為“任何語言”的運行平臺使用。

現在網絡上關于 Graal VM 的相關資料并不(bu)多,還是要看官方(fang)文(wen)檔。本文(wen)旨(zhi)在簡(jian)要介紹:

  • 什么是 Graal VM?
  • Graal VM 有什么好處?
  • Graal VM 有什么缺點?
  • Graal VM 的工作原理是什么?
  • 在 macOS 上安裝 Graal VM
  • 將基于 Spring Boot 的 Java 應用程序編譯成本地應用程序

思維導圖

下面是一張 Graal VM 的簡要思維導圖

 

一篇通俗易懂的文章:GraalVM:微服務(wu)時代的Java。

什么是 Graal VM

Graal VM 被官方稱為(wei)“Universal VM”和“Polyglot VM”,是一個在HotSpot虛(xu)擬機基礎上增強而成的跨語(yu)言全棧(zhan)虛(xu)擬機,口號是“Run Programs Faster Anywhere”。可以在 Graal VM 上運行(xing)“任何語(yu)言”,這些(xie)語(yu)言包(bao)括:

  • 基于 Java 虛擬機的語言:Java、Scala、Groovy、Kotlin 等;
  • 基于 LLVM 的語言:C、C++、Rust;
  • 其他語言:JavaScript、Ruby、Python和R語言等。

Graal VM可(ke)以無額外開(kai)銷地混合使用這(zhe)些編(bian)程語言,支持(chi)不(bu)同語言中混用對方的(de)接口和對象,也能夠支持(chi)這(zhe)些語言使用已經(jing)編(bian)寫好的(de)本(ben)地庫(ku)文(wen)件。

Graal VM 的好處

具體可參考官方文(wen)檔:Why GraalVM?

 

我認為最重要的特性是 Ahead-of-Time Compilation。Substrate VM 是一個在 Graal VM 0.20 版本里的極小型的運行時環境,包括了獨立的異常處理、同步調度、線程管理、內存管理(垃圾收集)和JNI訪問等組件。Substrate VM 還包含了一個本地鏡像的構造器(Native Image Generator),用(yong)戶(hu)可以通過本地鏡(jing)像構造(zao)器(qi)構建基于構建機器(qi)的可執行文件(jian)。

構(gou)造器(qi)采用(yong)指針(zhen)分析(Points-To Analysis)技術,從用(yong)戶提供的(de)(de)程序入口出發,搜索所(suo)有(you)可(ke)達的(de)(de)代碼(ma)(ma)。在(zai)(zai)搜索的(de)(de)同時(shi),它還將(jiang)執行(xing)(xing)初始化代碼(ma)(ma),并(bing)在(zai)(zai)最終生成(cheng)可(ke)執行(xing)(xing)文件時(shi),將(jiang)已初始化的(de)(de)堆(dui)保存至一個堆(dui)快照之中。

Substrate VM就可(ke)以直接(jie)從目標(biao)程(cheng)序(xu)開(kai)始(shi)運行,而無須重(zhong)復進行Java虛擬機的初始(shi)化過(guo)程(cheng)。但相應(ying)地,原理上也(ye)決定了Substrate VM必須要求(qiu)目標(biao)程(cheng)序(xu)是(shi)完全封閉的,即不(bu)能(neng)動態(tai)加載其他編(bian)譯(yi)(yi)期不(bu)可(ke)知的代碼和類庫。基于這(zhe)個假設,Substrate VM才能(neng)探索整個編(bian)譯(yi)(yi)空(kong)間,并通過(guo)靜(jing)態(tai)分析推算出所有(you)虛方法調(diao)用的目標(biao)方法。

使 Java 適應原生

以往單個服務(wu)需(xu)要 7*24 小時(shi)不間斷運行,需(xu)要單機高可用(yong),此時(shi) Java 服務(wu)就很適合。但是 Java 應用(yong)程(cheng)序都需(xu)要運行在上百兆的 JRE 上,在微(wei)服務(wu)上就并(bing)不合適。

同時在微服務中,應(ying)用可以(yi)隨時拆分(fen),每個(ge)應(ying)用并不(bu)需要很(hen)大的內存,而是需要快速啟動、隨時更新(xin),也可能不(bu)需要長時間運行。Java 應(ying)用程序本來啟動就很(hen)慢,同時需要充分(fen)預熱才能夠(gou)獲取高性能。

GraalVM 提前編(bian)譯(yi)就提供了一種(zhong)解(jie)決方(fang)案,官方(fang)給(gei)出使(shi)用了 GraalVm 后啟(qi)動時(shi)間能(neng)夠提高 50 倍,內(nei)存(cun)有 5 倍的下降(jiang)。

 

 

Graal VM 的缺點

Java 語(yu)言(yan)在(zai)微服(fu)務天(tian)生就有劣勢(shi)(shi),這(zhe)是(shi)因(yin)為(wei) Java 誕生之初的(de)(de)口號就是(shi)“一次編(bian)寫,到處運行”。這(zhe)個(ge)口號已經植入(ru) Java 的(de)(de)基因(yin)中(zhong)。如果想改變(bian)這(zhe)些(真的(de)(de)要拿Java的(de)(de)劣勢(shi)(shi)去和別(bie)的(de)(de)語(yu)言(yan)的(de)(de)優(you)勢(shi)(shi)相比),會有很多困難:

  • Java 語言的反射機制,使得在編譯期生成可執行文件很困難。因為通過反射機制可以在運行期間動態調用API接口,這些在編譯期是無法感知的。除非放棄反射機制,或者在編譯時提供配置文件供反射調用。
  • ASM、CGLIB、Javassist字節碼庫會在運行時生成、修改字節碼,這些也沒法通過 AOT 編譯成原生代碼。比如 Spring 的依賴注入就使用了 CGLIB 增強。Spring 已經在新版本中適配了 GraalVM,可以關閉 CGLIB。
  • 放棄 HotSpot 虛擬機本身的內部借款,因為在本地鏡像中,連 HotSpot 本身都被消滅了。
  • 啟動時間、內存使用確實有大幅度優化,但是對于長時間運行的大型應用,未必有 HotSpot 的 Java 應用程序速度快。

Graal VM 的工作原理

Graal VM的基本工作原(yuan)理是(shi)將(jiang)這些語言的源(yuan)代碼(ma)(例如JavaScript)或源(yuan)代碼(ma)編譯后的中(zhong)間格式(例如LLVM字節碼(ma))通過解釋器轉(zhuan)(zhuan)換(huan)為(wei)能被(bei)Graal VM接受的中(zhong)間表(biao)示(shi)(Intermediate Representation,IR),譬如設計一(yi)個解釋器專門對(dui)LLVM輸(shu)出的字節碼(ma)進行轉(zhuan)(zhuan)換(huan)來支持C和C++語言,這個過程稱為(wei)“程序特化”(Specialized,也常稱為(wei)Partial Evaluation)。

Graal VM提(ti)供了Truffle工具(ju)集來(lai)快速構建面向一種新語言的(de)解釋器,并用它構建了一個稱為Sulong的(de)高性能LLVM字節碼解釋器。

在 macOS 上安裝 Graal VM

Linux、Windows 等其他(ta)平臺可以(yi)參考 Install GraalVM。由(you)于我使(shi)用 macOS,本篇文章介紹如何在 macOS 上安裝 Graal VM,基于 OpenJDK 11 的(de) GraalVM Community Edition。

安裝 Graal VM

macOS 上的 GraalVM 社區版是 tar.gz 文件,JDK 的安裝目錄是:

/Library/Java/JavaVirtualMachines/<graalvm>/Contents/Home

x86 64位的 macOS 安裝步(bu)驟如下:

  1. 在 GraalVM Releases repository on GitHub 上找到 graalvm-ce-java11-darwin-amd64-20.1.0.tar.gz 下載。
  2. 解壓縮
    tar -xvf graalvm-ce-java11-darwin-amd64-20.1.0.tar.gz
  3. 將文件夾移動到 /Library/Java/JavaVirtualMachines 目錄下(需要使用 sudo)。
    sudo mv graalvm-ce-java11-20.1.0 /Library/Java/JavaVirtualMachines

檢測是否安裝(zhuang)成功(gong),可以運行命令:

/usr/libexec/java_home -V

運行結果為:

Matching Java Virtual Machines (2):

    11.0.7, x86_64:    "GraalVM CE 20.1.0"    /Library/Java/JavaVirtualMachines/graalvm-ce-java11-20.1.0/Contents/Home
    1.8.0_201, x86_64:    "Java SE 8"    /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home

/Library/Java/JavaVirtualMachines/graalvm-ce-java11-20.1.0/Contents/Home
  1. 由于機器上可能存在多個 JDK,需要配置運行環境。

將 GraalVM bin 目錄加入 PATH 環(huan)境變(bian)量。

export PATH=/Library/Java/JavaVirtualMachines/graalvm-ce-java11-20.1.0/Contents/Home/bin:$PATH

設置 JAVA_HOME 環境(jing)變量(liang)。

export JAVA_HOME=/Library/Java/JavaVirtualMachines/graalvm-ce-java11-20.1.0/Contents/Home

注意:可(ke)能需要修改 bashc 配置(zhi)文件。

安裝 GraalVM 組件

通過上述步驟,已經安裝好了 GraalVM 的基礎組件,如果需要額外支持 Python、R 等語言,需要使用 gu 組件。

gu install ruby

gu install r
gu install python
gu install wasm

安裝 GraalVM Native Image,運行命令:

gu install native-image

安裝 LLVM toolchain 組(zu)件,運(yun)行命令(ling):

gu install llvm-toolchain

將基于 Spring Boot 的 Java 應用程序編譯成本地應用程序

可以參(can)考 GitHub 的 spring-boot-graalvm 項(xiang)目(mu),這個項(xiang)目(mu)里詳細列出(chu)(chu)了 GraalVM 編譯 Spring Boot Java 應(ying)用程序(xu)可能出(chu)(chu)現的所有問題,并(bing)對比了 Java 應(ying)用啟動與編譯成本地可執行的 Java 程序(xu)。

Spring與Graal VM共同(tong)維(wei)護的(de)在Spring Graal Native項(xiang)目已經提供了大多數Spring Boot組件的(de)配置信息(以及(ji)一些(xie)需(xu)(xu)要(yao)(yao)在代(dai)碼層面處理的(de)Patch),我(wo)們只需(xu)(xu)要(yao)(yao)簡單依(yi)賴該(gai)工(gong)(gong)程即可。這樣 Graal VM 就(jiu)能獲取編譯(yi)期(qi)的(de)反射(she)、動(dong)態代(dai)理等配置。我(wo)們只需(xu)(xu)要(yao)(yao)簡單依(yi)賴工(gong)(gong)程即可。

需(xu)要在 pom.xml 中增加依賴:

<dependency>

    <groupId>org.springframework</groupId>
    <artifactId>spring-context-indexer</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.springframework.experimental</groupId>
    <artifactId>spring-graalvm-native</artifactId>
    <version>0.7.1</version>
</dependency>

指定啟動類的路徑:

<properties>

    <start-class>com.yano.workflow.WorkflowApplication</start-class>
</properties>

配置一個獨立的 profile,在編譯時通過 native-image-maven-plugin 插(cha)件將其編譯成本地可執(zhi)行文(wen)件。

<profiles>

    <profile>
        <id>native</id>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.graalvm.nativeimage</groupId>
                    <artifactId>native-image-maven-plugin</artifactId>
                    <version>20.1.0</version>
                    <configuration>
                        <buildArgs>-J-Xmx4G -H:+TraceClassInitialization
                            -H:+ReportExceptionStackTraces
                            -Dspring.graal.remove-unused-autoconfig=true
                            -Dspring.graal.remove-yaml-support=true
                        </buildArgs>
                        <imageName>${project.artifactId}</imageName>
                    </configuration>
                    <executions>
                        <execution>
                            <goals>
                                <goal>native-image</goal>
                            </goals>
                            <phase>package</phase>
                        </execution>
                    </executions>
                </plugin>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>

該插件在 Maven 中(zhong)央倉(cang)庫不存在,需要指(zhi)定 pluginRepositories 和 repositories:

<repositories>

    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>repo.spring.io/milestone</url>
    </repository>
</repositories>
<pluginRepositories>
    <pluginRepository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>repo.spring.io/milestone</url>
    </pluginRepository>
</pluginRepositories>

Graal VM不支持CGLIB,只能使用JDK動態代理,所以應當把Spring對普通類的Bean增強給關閉掉。Spring Boot 的版本要大于等于 2.2,SpringBootApplication 注解上(shang)將(jiang) proxyBeanMethods 參數設置為 false。

@SpringBootApplication(proxyBeanMethods = false)

public class SpringBootHelloApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootHelloApplication.class, args);
    }

}

在命(ming)令行通過 maven 打(da)包項目:

mvn -Pnative clean package

最終(zhong)在 target 目錄能(neng)夠看到可執行(xing)文件,大(da)概在 50M 左右(you),相比(bi) fat jar 為 17M。

 

 

java -jar target/spring-boot-graal-0.0.1-SNAPSHOT.jar

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::             (v2.3.0.M4)

2020-04-30 15:40:21.187  INFO 40149 --- [           main] i.j.s.SpringBootHelloApplication         : Starting SpringBootHelloApplication v0.0.1-SNAPSHOT on PikeBook.fritz.box with PID 40149 (/Users/jonashecht/dev/spring-boot/spring-boot-graalvm/target/spring-boot-graal-0.0.1-SNAPSHOT.jar started by jonashecht in /Users/jonashecht/dev/spring-boot/spring-boot-graalvm)
2020-04-30 15:40:21.190  INFO 40149 --- [           main] i.j.s.SpringBootHelloApplication         : No active profile set, falling back to default profiles: default
2020-04-30 15:40:22.280  INFO 40149 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 8080
2020-04-30 15:40:22.288  INFO 40149 --- [           main] i.j.s.SpringBootHelloApplication         : Started SpringBootHelloApplication in 1.47 seconds (JVM running for 1.924)

能夠通過命令行直接運行程序,啟動速度賊快。對比 Hello World web 普通應用程序,啟動時間是 1.47s,占用內存 491 MB

而編譯成本地代碼的 Spring Boot 程序,啟動速度是 0.078s,占用內存 30 MB

./spring-boot-graal

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::

2020-05-01 10:25:31.200  INFO 42231 --- [           main] i.j.s.SpringBootHelloApplication         : Starting SpringBootHelloApplication on PikeBook.fritz.box with PID 42231 (/Users/jonashecht/dev/spring-boot/spring-boot-graalvm/target/native-image/spring-boot-graal started by jonashecht in /Users/jonashecht/dev/spring-boot/spring-boot-graalvm/target/native-image)
2020-05-01 10:25:31.200  INFO 42231 --- [           main] i.j.s.SpringBootHelloApplication         : No active profile set, falling back to default profiles: default
2020-05-01 10:25:31.241  WARN 42231 --- [           main] io.netty.channel.DefaultChannelId        : Failed to find the current process ID from ''; using a random value: 635087100
2020-05-01 10:25:31.245  INFO 42231 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 8080
2020-05-01 10:25:31.245  INFO 42231 --- [           main] i.j.s.SpringBootHelloApplication         : Started SpringBootHelloApplication in 0.078 seconds (JVM running for 0.08)

總結

  • 本篇文章主要討論 GraalVM 和 Java 的關系,GraalVM 上能夠運行很多語言,可參考Why GraalVM。
  • 注意 Graal 的環境變量配置,配置錯誤的話,是沒法編譯的,同時 JDK 11 需要高版本的 maven 版本。
  • Graal VM 和 GraalVM 是一個東東,官網是叫 GraalVM,但是其他地方都是 Graal VM……
  • 為了適應原生,JDK 自身也在演進。
  • GraalVM 編譯的 Java 本地應用僅適用于一次性運行、短時間運行的場景,長時間運行還是傳統 Java 程序效率高。
  • 本篇文章的 GitHub 地址:LjyYano/Thinking_in_Java_MindMapping
0條評論
0 / 1000
白龍馬
14文章數
0粉絲數(shu)
白龍馬
14 文(wen)章 | 0 粉絲

通過 GraalVM 將 Java 程序編譯成本地機器碼!

2023-12-12 02:09:33
71
0

前言

2018年4月(yue),Oracle Labs新公開(kai)了一項黑(hei)科技:Graal VM。

這是一個在HotSpot虛擬機(ji)基礎上增強而成的(de)跨語言(yan)全棧(zhan)虛擬機(ji),可(ke)以作(zuo)為(wei)“任何(he)語言(yan)”的(de)運行平臺使用。

現在網(wang)絡上關于 Graal VM 的相關資料并不(bu)多,還是(shi)要看官(guan)方文檔。本文旨在簡要介紹(shao):

  • 什么是 Graal VM?
  • Graal VM 有什么好處?
  • Graal VM 有什么缺點?
  • Graal VM 的工作原理是什么?
  • 在 macOS 上安裝 Graal VM
  • 將基于 Spring Boot 的 Java 應用程序編譯成本地應用程序

思維導圖

下面是一張 Graal VM 的簡要思維導圖

 

一篇通俗易(yi)懂的文章:GraalVM:微服務時(shi)代的Java。

什么是 Graal VM

Graal VM 被官方稱為(wei)“Universal VM”和“Polyglot VM”,是一個(ge)在HotSpot虛(xu)擬機基礎上增強而(er)成的跨語(yu)言(yan)(yan)全棧(zhan)虛(xu)擬機,口號是“Run Programs Faster Anywhere”。可以(yi)在 Graal VM 上運行“任何語(yu)言(yan)(yan)”,這些語(yu)言(yan)(yan)包括(kuo):

  • 基于 Java 虛擬機的語言:Java、Scala、Groovy、Kotlin 等;
  • 基于 LLVM 的語言:C、C++、Rust;
  • 其他語言:JavaScript、Ruby、Python和R語言等。

Graal VM可以無額外開銷地混(hun)合使(shi)用(yong)這些編(bian)程語(yu)言,支(zhi)持不同語(yu)言中混(hun)用(yong)對(dui)方的(de)接口和對(dui)象,也能夠支(zhi)持這些語(yu)言使(shi)用(yong)已經編(bian)寫好的(de)本地庫文件。

Graal VM 的好處

具體可參考官方(fang)文檔:Why GraalVM?

 

我認為最重要的特性是 Ahead-of-Time Compilation。Substrate VM 是一個在 Graal VM 0.20 版本里的極小型的運行時環境,包括了獨立的異常處理、同步調度、線程管理、內存管理(垃圾收集)和JNI訪問等組件。Substrate VM 還包含了一個本地鏡像的構造器(Native Image Generator),用戶可(ke)以通過本地鏡(jing)像構造(zao)器(qi)(qi)構建基于構建機器(qi)(qi)的可(ke)執(zhi)行文(wen)件。

構造器采用(yong)指(zhi)針分析(Points-To Analysis)技術(shu),從用(yong)戶提供的(de)程序入口出發,搜索所有可達的(de)代碼(ma)。在(zai)搜索的(de)同時,它還將執行(xing)初(chu)始化代碼(ma),并在(zai)最(zui)終生成可執行(xing)文件(jian)時,將已初(chu)始化的(de)堆(dui)保存至一個(ge)堆(dui)快照之中。

Substrate VM就可以直接從目(mu)(mu)標(biao)程(cheng)序(xu)開始運行(xing),而無(wu)須重復(fu)進行(xing)Java虛擬機(ji)的初(chu)始化過(guo)程(cheng)。但相(xiang)應地(di),原理(li)上也(ye)決定了Substrate VM必須要求目(mu)(mu)標(biao)程(cheng)序(xu)是完全封閉的,即不能動態加載其他編(bian)譯(yi)(yi)期不可知(zhi)的代碼和類庫。基于這個(ge)(ge)假(jia)設(she),Substrate VM才能探索(suo)整個(ge)(ge)編(bian)譯(yi)(yi)空間,并通過(guo)靜態分(fen)析(xi)推算出所有(you)虛方法調用的目(mu)(mu)標(biao)方法。

使 Java 適應原生

以往單個服務需要(yao) 7*24 小(xiao)時(shi)不間斷運(yun)行,需要(yao)單機高可用,此時(shi) Java 服務就很適合。但是 Java 應用程序都需要(yao)運(yun)行在上(shang)百兆的 JRE 上(shang),在微服務上(shang)就并不合適。

同時(shi)在(zai)微服務中,應用(yong)(yong)可以隨(sui)時(shi)拆分(fen),每(mei)個應用(yong)(yong)并(bing)不需(xu)要(yao)很大(da)的內存,而是需(xu)要(yao)快速(su)啟(qi)動、隨(sui)時(shi)更新,也可能不需(xu)要(yao)長時(shi)間(jian)運(yun)行。Java 應用(yong)(yong)程序本來啟(qi)動就很慢,同時(shi)需(xu)要(yao)充分(fen)預(yu)熱才能夠(gou)獲取高性能。

GraalVM 提(ti)前編譯就提(ti)供了一種解決方案,官方給出使用了 GraalVm 后(hou)啟動時間(jian)能(neng)夠(gou)提(ti)高 50 倍,內存有 5 倍的下降。

 

 

Graal VM 的缺點

Java 語言在(zai)微服務天生(sheng)就(jiu)有劣(lie)勢(shi),這(zhe)是因為 Java 誕生(sheng)之初的口號就(jiu)是“一次編寫,到處運(yun)行”。這(zhe)個口號已經植入 Java 的基因中。如(ru)果想改變(bian)這(zhe)些(xie)(真的要拿(na)Java的劣(lie)勢(shi)去和別的語言的優勢(shi)相比(bi)),會有很多困難:

  • Java 語言的反射機制,使得在編譯期生成可執行文件很困難。因為通過反射機制可以在運行期間動態調用API接口,這些在編譯期是無法感知的。除非放棄反射機制,或者在編譯時提供配置文件供反射調用。
  • ASM、CGLIB、Javassist字節碼庫會在運行時生成、修改字節碼,這些也沒法通過 AOT 編譯成原生代碼。比如 Spring 的依賴注入就使用了 CGLIB 增強。Spring 已經在新版本中適配了 GraalVM,可以關閉 CGLIB。
  • 放棄 HotSpot 虛擬機本身的內部借款,因為在本地鏡像中,連 HotSpot 本身都被消滅了。
  • 啟動時間、內存使用確實有大幅度優化,但是對于長時間運行的大型應用,未必有 HotSpot 的 Java 應用程序速度快。

Graal VM 的工作原理

Graal VM的基本工作原理是將這(zhe)些語(yu)言(yan)的源(yuan)(yuan)代(dai)(dai)碼(ma)(ma)(例(li)如(ru)JavaScript)或源(yuan)(yuan)代(dai)(dai)碼(ma)(ma)編譯后的中間格(ge)式(例(li)如(ru)LLVM字(zi)節碼(ma)(ma))通過解釋器(qi)轉換為(wei)能(neng)被Graal VM接受的中間表示(Intermediate Representation,IR),譬如(ru)設計一個解釋器(qi)專門對(dui)LLVM輸出的字(zi)節碼(ma)(ma)進行轉換來(lai)支持C和C++語(yu)言(yan),這(zhe)個過程稱為(wei)“程序特化”(Specialized,也(ye)常稱為(wei)Partial Evaluation)。

Graal VM提供了(le)Truffle工具集(ji)來(lai)快速(su)構建(jian)面向一種(zhong)新語(yu)言的解(jie)釋器,并用(yong)它構建(jian)了(le)一個稱(cheng)為Sulong的高性能(neng)LLVM字節碼(ma)解(jie)釋器。

在 macOS 上安裝 Graal VM

Linux、Windows 等其他平臺可以參考 Install GraalVM。由于(yu)我使(shi)用 macOS,本篇文章介紹如何(he)在 macOS 上安裝 Graal VM,基于(yu) OpenJDK 11 的(de) GraalVM Community Edition。

安裝 Graal VM

macOS 上的 GraalVM 社區版是 tar.gz 文件,JDK 的安(an)裝(zhuang)目錄(lu)是:

/Library/Java/JavaVirtualMachines/<graalvm>/Contents/Home

x86 64位的(de) macOS 安裝步(bu)驟(zou)如下:

  1. 在 GraalVM Releases repository on GitHub 上找到 graalvm-ce-java11-darwin-amd64-20.1.0.tar.gz 下載。
  2. 解壓縮
    tar -xvf graalvm-ce-java11-darwin-amd64-20.1.0.tar.gz
  3. 將文件夾移動到 /Library/Java/JavaVirtualMachines 目錄下(需要使用 sudo)。
    sudo mv graalvm-ce-java11-20.1.0 /Library/Java/JavaVirtualMachines

檢測是否安裝成功,可以運行命(ming)令:

/usr/libexec/java_home -V

運行結果為:

Matching Java Virtual Machines (2):

    11.0.7, x86_64:    "GraalVM CE 20.1.0"    /Library/Java/JavaVirtualMachines/graalvm-ce-java11-20.1.0/Contents/Home
    1.8.0_201, x86_64:    "Java SE 8"    /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home

/Library/Java/JavaVirtualMachines/graalvm-ce-java11-20.1.0/Contents/Home
  1. 由于機器上可能存在多個 JDK,需要配置運行環境。

將 GraalVM bin 目錄加入 PATH 環(huan)境(jing)變量(liang)。

export PATH=/Library/Java/JavaVirtualMachines/graalvm-ce-java11-20.1.0/Contents/Home/bin:$PATH

設置 JAVA_HOME 環境變量。

export JAVA_HOME=/Library/Java/JavaVirtualMachines/graalvm-ce-java11-20.1.0/Contents/Home

注意:可能需要修改 bashc 配置文件(jian)。

安裝 GraalVM 組件

通過上述步驟,已經安裝好了 GraalVM 的基礎組件,如果需要額外支持 Python、R 等語言,需要使用 gu 組件。

gu install ruby

gu install r
gu install python
gu install wasm

安裝 GraalVM Native Image,運行命令:

gu install native-image

安裝 LLVM toolchain 組件,運行(xing)命令:

gu install llvm-toolchain

將基于 Spring Boot 的 Java 應用程序編譯成本地應用程序

可(ke)以參考 GitHub 的 spring-boot-graalvm 項目(mu),這(zhe)個項目(mu)里詳細列出(chu)了 GraalVM 編譯(yi) Spring Boot Java 應用(yong)程序可(ke)能出(chu)現的所有問題,并對(dui)比(bi)了 Java 應用(yong)啟動與編譯(yi)成本地可(ke)執行的 Java 程序。

Spring與Graal VM共同(tong)維護(hu)的在(zai)Spring Graal Native項(xiang)目已經(jing)提供了大多數Spring Boot組件的配置(zhi)信息(以(yi)及一些需(xu)要在(zai)代(dai)碼層面處(chu)理的Patch),我們只需(xu)要簡單依賴(lai)該工程(cheng)即(ji)可。這樣 Graal VM 就(jiu)能獲取(qu)編(bian)譯期(qi)的反射、動態代(dai)理等(deng)配置(zhi)。我們只需(xu)要簡單依賴(lai)工程(cheng)即(ji)可。

需要在(zai) pom.xml 中增加依賴:

<dependency>

    <groupId>org.springframework</groupId>
    <artifactId>spring-context-indexer</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.springframework.experimental</groupId>
    <artifactId>spring-graalvm-native</artifactId>
    <version>0.7.1</version>
</dependency>

指定啟動類的路徑:

<properties>

    <start-class>com.yano.workflow.WorkflowApplication</start-class>
</properties>

配置一個獨立的 profile,在編譯時通過 native-image-maven-plugin 插件將其編譯成(cheng)本地可執(zhi)行文件。

<profiles>

    <profile>
        <id>native</id>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.graalvm.nativeimage</groupId>
                    <artifactId>native-image-maven-plugin</artifactId>
                    <version>20.1.0</version>
                    <configuration>
                        <buildArgs>-J-Xmx4G -H:+TraceClassInitialization
                            -H:+ReportExceptionStackTraces
                            -Dspring.graal.remove-unused-autoconfig=true
                            -Dspring.graal.remove-yaml-support=true
                        </buildArgs>
                        <imageName>${project.artifactId}</imageName>
                    </configuration>
                    <executions>
                        <execution>
                            <goals>
                                <goal>native-image</goal>
                            </goals>
                            <phase>package</phase>
                        </execution>
                    </executions>
                </plugin>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>

該插件(jian)在(zai) Maven 中(zhong)央倉庫不存在(zai),需要指定 pluginRepositories 和 repositories:

<repositories>

    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>repo.spring.io/milestone</url>
    </repository>
</repositories>
<pluginRepositories>
    <pluginRepository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>repo.spring.io/milestone</url>
    </pluginRepository>
</pluginRepositories>

Graal VM不支持CGLIB,只能使用JDK動態代理,所以應當把Spring對普通類的Bean增強給關閉掉。Spring Boot 的版本要大于等于 2.2,SpringBootApplication 注(zhu)解上將 proxyBeanMethods 參數設置為 false。

@SpringBootApplication(proxyBeanMethods = false)

public class SpringBootHelloApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootHelloApplication.class, args);
    }

}

在命令行通過 maven 打包(bao)項目:

mvn -Pnative clean package

最終在(zai) target 目錄能(neng)夠看到可執(zhi)行文件,大概在(zai) 50M 左右,相(xiang)比(bi) fat jar 為(wei) 17M。

 

 

java -jar target/spring-boot-graal-0.0.1-SNAPSHOT.jar

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::             (v2.3.0.M4)

2020-04-30 15:40:21.187  INFO 40149 --- [           main] i.j.s.SpringBootHelloApplication         : Starting SpringBootHelloApplication v0.0.1-SNAPSHOT on PikeBook.fritz.box with PID 40149 (/Users/jonashecht/dev/spring-boot/spring-boot-graalvm/target/spring-boot-graal-0.0.1-SNAPSHOT.jar started by jonashecht in /Users/jonashecht/dev/spring-boot/spring-boot-graalvm)
2020-04-30 15:40:21.190  INFO 40149 --- [           main] i.j.s.SpringBootHelloApplication         : No active profile set, falling back to default profiles: default
2020-04-30 15:40:22.280  INFO 40149 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 8080
2020-04-30 15:40:22.288  INFO 40149 --- [           main] i.j.s.SpringBootHelloApplication         : Started SpringBootHelloApplication in 1.47 seconds (JVM running for 1.924)

能夠通過命令行直接運行程序,啟動速度賊快。對比 Hello World web 普通應用程序,啟動時間是 1.47s,占用內存 491 MB

而編譯成本地代碼的 Spring Boot 程序,啟動速度是 0.078s,占用內存 30 MB

./spring-boot-graal

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::

2020-05-01 10:25:31.200  INFO 42231 --- [           main] i.j.s.SpringBootHelloApplication         : Starting SpringBootHelloApplication on PikeBook.fritz.box with PID 42231 (/Users/jonashecht/dev/spring-boot/spring-boot-graalvm/target/native-image/spring-boot-graal started by jonashecht in /Users/jonashecht/dev/spring-boot/spring-boot-graalvm/target/native-image)
2020-05-01 10:25:31.200  INFO 42231 --- [           main] i.j.s.SpringBootHelloApplication         : No active profile set, falling back to default profiles: default
2020-05-01 10:25:31.241  WARN 42231 --- [           main] io.netty.channel.DefaultChannelId        : Failed to find the current process ID from ''; using a random value: 635087100
2020-05-01 10:25:31.245  INFO 42231 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 8080
2020-05-01 10:25:31.245  INFO 42231 --- [           main] i.j.s.SpringBootHelloApplication         : Started SpringBootHelloApplication in 0.078 seconds (JVM running for 0.08)

總結

  • 本篇文章主要討論 GraalVM 和 Java 的關系,GraalVM 上能夠運行很多語言,可參考Why GraalVM。
  • 注意 Graal 的環境變量配置,配置錯誤的話,是沒法編譯的,同時 JDK 11 需要高版本的 maven 版本。
  • Graal VM 和 GraalVM 是一個東東,官網是叫 GraalVM,但是其他地方都是 Graal VM……
  • 為了適應原生,JDK 自身也在演進。
  • GraalVM 編譯的 Java 本地應用僅適用于一次性運行、短時間運行的場景,長時間運行還是傳統 Java 程序效率高。
  • 本篇文章的 GitHub 地址:LjyYano/Thinking_in_Java_MindMapping
文章來自個人專欄
文(wen)章 | 訂(ding)閱
0條評論
0 / 1000
請輸入你的評論
0
0