- 適用於 JDK 23 的 GraalVM (最新版)
- 適用於 JDK 24 的 GraalVM (搶先體驗版)
- 適用於 JDK 21 的 GraalVM
- 適用於 JDK 17 的 GraalVM
- 封存
- 開發組建
執行示範應用程式
Espresso 是 Java 虛擬機器規格的實作,除了能夠在 Java 或其他 JVM 語言中執行應用程式之外,還提供一些有趣的功能。例如,增強的 HotSwap 功能 可透過啟用無限的熱碼重新載入來提高開發人員的生產力。此外,為了說明 Espresso 可以做什麼,請考慮以下簡短的範例。
混合 Java 的 AOT 和 JIT #
GraalVM Native Image 技術允許將應用程式預先 (AOT) 編譯為可執行原生二進位檔,這些檔案
- 是獨立的
- 立即啟動
- 具有較低的記憶體使用量
使用 Native Image 的主要缺點是,程式的分析和編譯是在封閉世界假設下進行的,這表示靜態分析需要處理應用程式中將執行的所有位元組碼。這使得使用某些語言功能(例如動態類別載入或反射)變得棘手。
Espresso 是基於 Truffle 框架建構的 JVM 位元組碼直譯器 JVM 實作。它本質上是一個 Java 應用程式,Truffle 框架本身和 GraalVM JIT 編譯器也是如此。這三者都可以使用 native-image
預先編譯。在應用程式的某些部分使用 Espresso 可以隔離所需的動態行為,並且仍然可以在其餘程式碼上使用原生可執行檔。
以典型的 Java Shell 工具 (JShell) 為例,它是一個能夠評估 Java 程式碼的 REPL,由兩個部分組成
- UI - 處理輸入輸出的 CLI 應用程式
- 用於執行您輸入 Shell 的程式碼的後端處理器。
此設計自然適合我們想要說明的重點。我們可以建構 JShell UI 部分的原生可執行檔,並使其包含 Espresso 以動態執行在執行階段指定的程式碼。
先決條件
使用示範應用程式複製 專案,並導覽至 espresso-jshell
目錄
git clone https://github.com/graalvm/graalvm-demos.git
cd graalvm-demos/espresso-jshell
JShell 實作實際上是正常的 JShell 啟動器程式碼,它只接受執行引擎的 Espresso 實作。
將 AOT 編譯部分與動態評估程式碼的元件連結在一起的「膠水」程式碼位於 EspressoExecutionControl
類別中。它會在 Espresso 環境中載入 JShell 類別,並將輸入委派給它們
protected final Lazy<Value> ClassBytecodes = Lazy.of(() -> loadClass("jdk.jshell.spi.ExecutionControl$ClassBytecodes"));
protected final Lazy<Value> byte_array = Lazy.of(() -> loadClass("[B"));
protected final Lazy<Value> ExecutionControlException = Lazy.of(() -> loadClass("jdk.jshell.spi.ExecutionControl$ExecutionControlException"));
protected final Lazy<Value> RunException = Lazy.of(() -> loadClass("jdk.jshell.spi.ExecutionControl$RunException"));
protected final Lazy<Value> ClassInstallException = Lazy.of(() -> loadClass("jdk.jshell.spi.ExecutionControl$ClassInstallException"));
protected final Lazy<Value> NotImplementedException = Lazy.of(() -> loadClass("jdk.jshell.spi.ExecutionControl$NotImplementedException"));
protected final Lazy<Value> EngineTerminationException = Lazy.of(() -> loadClass("jdk.jshell.spi.ExecutionControl$EngineTerminationException"));
protected final Lazy<Value> InternalException = Lazy.of(() -> loadClass("jdk.jshell.spi.ExecutionControl$InternalException"));
protected final Lazy<Value> ResolutionException = Lazy.of(() -> loadClass("jdk.jshell.spi.ExecutionControl$ResolutionException"));
protected final Lazy<Value> StoppedException = Lazy.of(() -> loadClass("jdk.jshell.spi.ExecutionControl$StoppedException"));
protected final Lazy<Value> UserException = Lazy.of(() -> loadClass("jdk.jshell.spi.ExecutionControl$UserException"));
還有更多程式碼可以正確傳遞值並轉換例外狀況。若要試用,請使用提供的腳本建構 espresso-jshell
二進位檔,該腳本將
- 將 Java 來源建構成位元組碼
- 建構 JAR 檔案
- 建構原生可執行檔
建構後,您可以觀察產生的二進位檔(file
和 ldd
是 Linux 命令)
file ./espresso-jshell
ldd ./espresso-jshell
它確實是一個不依賴 JVM 的二進位檔,您可以執行它,並注意到它啟動的速度有多快
./espresso-jshell
| Welcome to JShell -- Version 11.0.10
| For an introduction type: /help intro
jshell> 1 + 1
1 ==> 2
實驗將新程式碼載入 JShell,並查看 Espresso 如何執行它。
觀看混合 AOT 和 JIT 編譯程式碼與 Espresso 示範的影片版本。
搭配 Espresso 的 GraalVM 工具 #
Espresso 是 GraalVM 生態系統的適當部分,並且與其他 GraalVM 支援的語言一樣,預設會取得開發人員工具的支援。Truffle 框架與除錯器、分析器、記憶體分析器、Instrumentation API 等工具整合。語言的直譯器需要用一些註釋標記 AST 節點,以支援這些工具。
例如,若要能夠使用分析器,語言直譯器需要標記根節點。為了除錯目的,應該將語言表達式標記為可檢測的,並指定變數的範圍等等。語言直譯器不需要與工具本身整合。因此,您可以開箱即用地使用 CPU 取樣器或記憶體追蹤器工具,在 Espresso 上分析 Java 應用程式。
例如,如果我們有一個類似以下計算質數的類別
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.LongStream;
public class Main {
public static void main(String[] args) {
Main m = new Main();
for (int i = 0; i < 100_000; i++) {
System.out.println(m.random(100));
}
}
private Random r = new Random(41);
public List<Long> random(int upperbound) {
int to = 2 + r.nextInt(upperbound - 2);
int from = 1 + r.nextInt(to - 1);
return primeSequence(from, to);
}
public static List<Long> primeSequence(long min, long max) {
return LongStream.range(min, max)
.filter(Main::isPrime)
.boxed()
.collect(Collectors.toList());
}
public static boolean isPrime(long n) {
return LongStream.rangeClosed(2, (long) Math.sqrt(n))
.allMatch(i -> n % i != 0);
}
}
建構此程式,並使用 --cpusampler
選項執行它。
javac Main.java
java -truffle --cpusampler Main > output.txt
在 output.txt
檔案的結尾,您會找到分析器的輸出、方法的直方圖以及執行所花費的時間。您也可以嘗試使用 --memtracer
選項進行實驗,以查看此程式中配置發生在何處。
java -truffle --experimental-options --memtracer Main > output.txt
GraalVM 提供的其他工具包括 Chrome 除錯器、程式碼涵蓋率 和 GraalVM Insight。
由於對開發人員工具的「開箱即用」支援,Espresso 成為 JVM 的有趣選擇。
觀看 Espresso 的 GraalVM 內建工具的簡短示範。