【譯】GraalVM—下一代JVM介紹

原標題:GraalVM – an introduction to the next level JVM

隨著Red Hat宣布Quarkus作為…

為GraalVM和HotSpot量身訂製的下一代Kubernetes原生Java框架,使用一流的Java庫和標準構建

//quarkus.io

Red Hat展示的Quarkus示例項目的啟動速度和記憶體消耗給我留下了深刻的印象。令人印象深刻的主要原因之一是,程式碼是用GraalVM提前(ahead-of-time,AOT)編譯成本機映像(native image)的。為了幫助您更好地了解傳統的HotSpot JVM和GraalVM之間的區別,我將在此部落格文章中向您介紹GraalVM及其功能和歷史。

TL; DR:GraalVM是Oracle開發的用純Java編寫的JVM擴展,支援多語言編程和提前編譯。

HotSpot Java虛擬機的歷史

多年來,HotSpot是Oracle維護和分發的主要Java虛擬機,用於運行Java程式。 Java HotSpot Performance Engine於1999年發布,最初由Animorphic開發,該公司被Sun Microsystems收購,現在由Oracle擁有。該虛擬機主要用C / C ++編寫,並且變得越來越複雜(2007年估計有250.000行程式碼)。

HotSpot JVM的主要目的是運行Java位元組碼(.class文件)並持續分析程式的性能,以查找程式中經常執行的所謂熱點,並即時(JIT,全稱just-in-time)將其編譯為本機程式碼(機器程式碼)以提高性能。這是在運行時完成的,而不是在Java程式執行之前執行的,因此是即時(just-in-time)的。

在HotSpot JVM中運行Java程式碼的工作流程如下所示(簡化):

img

HotSpot虛擬機主要解釋程式提供的Java位元組碼,但在程式運行過程中發現有適合優化的部分時,也會及時將這部分位元組碼編譯為機器程式碼。

當使用JIT編譯器編譯一個方法時,當該方法被調用時,jvm將直接執行編譯出來的機器碼,而不是解釋它以此來提高性能。由於編譯本機程式碼需要CPU時間和記憶體,JVM必須在運行時決定編譯哪些方法,因為將所有方法直接編譯為本機程式碼會影響性能。

Java 9發行版中的更改

藉助Java 9,特別是JEP 295,JDK獲得了提前(ahead-of-time,AOT)編譯器jaotc。該編譯器使用OpenJDK項目Graal進行後端程式碼生成,這樣做的原因如下:

JIT編譯器速度很快,但是Java程式可能非常龐大,以至於JIT完全預熱需要很長時間。很少使用的Java方法可能根本不會被編譯,由於重複的解釋調用可能會導致性能下降

//openjdk.java.net/jeps/295

Graal OpenJDK項目演示了用純Java編寫的編譯器可以生成高度優化的程式碼。使用此AOT編譯器和Java 9,您可以提前手動編譯Java程式碼。這意味著在執行之前生成機器程式碼,而不是像JIT編譯器那樣在運行時生成程式碼,這是第一種實驗性的方法。

# using the new AOT compiler (jaotc is bundeled within JDK 9 and above)
jaotc --output libHelloWorld.so HelloWorld.class
jaotc --output libjava.base.so --module java.base
 
# with Java 9 you have to manually specify the location of the native code
java -XX:AOTLibrary=./libHelloWorld.so,./libjava.base.so HelloWorld

這將改善啟動時間,因為JIT編譯器不必攔截程式的執行。這種方法的主要缺點是生成的機器程式碼依賴於程式所在的平台(Linux,MacOS,windows…)。這可能導致AOT編譯程式碼與特定平台綁定。

img

GraalVM的架構

基於Graal編譯器,Oracle開始開發GraalVM,不僅與HotSpots JVM的複雜C/C++程式碼庫一起工作,而且還可以通過用Java編寫的虛擬機解決當前的多語言遷移問題。

GraalVM的架構如下所示:

img

img

首先,您可能會注意到一些非JVM語言的存在。現在可以在這個通用虛擬機中運行Ruby、R或JavaScript程式碼。只是因為GraalVM採用了Truffle框架。Truffle是一個開源庫,用於構建程式語言實現,作為自修改(self-modifying)抽象語法樹的解釋器。有了這個特性,您現在可以在Java程式碼庫中編寫和執行例如JavaScript程式碼。

此外,GraalVM提供了以下功能,可以提前將程式編譯成本機可執行文件:

GraalVM允許您提前將程式編譯為本地可執行文件。生成的程式不能在Java HotSpot VM上運行,而是使用必要的組件,例如記憶體管理,來自另一種虛擬機實現的執行緒調度(稱為Substrate VM)。SubstrateVM用Java編寫,然後編譯進本地可執行文件。與Java VM相比,生成的程式具有更快的啟動時間和更低的運行時記憶體開銷。

//www.graalvm.org/docs/reference-manual/aot-compilation/

使用GraalVM編譯並運行第一個Java程式

撰寫本文時,GraalVM有兩個版本:社區版(CE)和企業版(EE),僅適用於Mac OS X和Linux。要在開發過程中在Windows上使用GraalVM,您可以使用Oracle的官方Docker映像,以下示例中使用了該映像。

想像下面的簡單HelloWorld類:

public class HelloWorld {
  public static void main(String[] args) {
    System.out.println("Hello World!");
  }
}

在GraalVM和Java 9的AOT編譯器之前,您執行了如下程式碼:

$ javac HelloWorld.java
$ java HelloWorld
Hello World!

藉助GraalVM,您現在可以選擇使用現有方式(HotSpot JVM)運行應用程式,或者使用GraalVM AOT編譯器創建本機映像並運行可執行文件:

$ javac HelloWorld
$ native-image HelloWorld
$ ./helloworld
HelloWorld!

在這個HelloWorld示例中,改進的性能是微不足道的,但是在更大和更現實的應用程式中,性能的改進是顯著的。

在官方的GraalVM入門指南中可以找到一個簡單的多語言應用程式示例:

import java.io.*;
import java.util.stream.*;
import org.graalvm.polyglot.*;
 
public class PrettyPrintJSON {
  public static void main(String[] args) throws java.io.IOException {
      BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
      String input = reader.lines().collect(Collectors.joining(System.lineSeparator()));
      try (Context context = Context.create("js")) {
        Value parse = context.eval("js", "JSON.parse");
        Value stringify = context.eval("js", "JSON.stringify");
        Value result = stringify.execute(parse.execute(input));
        System.out.println(result.asString());
      }
    }
}

這個Java類負責漂亮地列印JSON,使用JavaScript方法JSON.parse() 和JSON.stringify() 。此類的本機鏡像可通過如下方式構建:

$ javac PrettyPrintJSON.java
$ native-image --language:js PrettyPrintJSON
$ ./prettyprintjson < prettyMe.json
{
  "GraalVM": {
    "description": "Language Abstraction Platform",
    "supports": [
      "combining languages",
      "embedding languages",
      "creating native images"
    ],
    "languages": [
      "Java",
      "JavaScript",
      "Node.js",
      "Python",
      "Ruby",
      "R",
      "LLVM"
    ]
  }
}

現在可以測量運行本機映像和在HotSpot中運行應用程式之間的性能差異:

$ time bin/java PrettyPrintJSON < prettyMe.json > /dev/null
real    0m1.101s
user    0m2.471s
sys 0m0.237s
 
$ time ./prettyprintjson < prettyMe.json > /dev/null
real    0m0.037s
user    0m0.015s
sys 0m0.016s

在我看來,Oracle和GraalVM在Java作為程式語言的主導地位方面做得非常好。此外,這一舉措提高了Java語言本身的可持續性和特性開發。有了多語言體系結構,這也增加了其他程式語言的採用。

您可以在我的GitHub存儲庫中找到示例,然後直接在GraalVM上(Mac和Linux)或在Windows上的Docker上(確保為Docker提供至少6 GB的RAM和2-4個內核)進行嘗試。

原文地址://rieckpil.de/whatis-graalvm/


🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟

歡迎訪問筆者部落格:blog.dongxishaonian.tech

關注筆者公眾號,推送各類原創/優質技術文章 ⬇️

WechatIMG6