<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【譯】使用 GraalVM 的高性能 Java

          共 12594字,需瀏覽 26分鐘

           ·

          2021-07-10 14:53

          一、前言

          GraalVM Native Image 可以成為 Java 云應(yīng)用程序的一個(gè)引人注目的平臺(tái)。正如我在 “GraalVM: Native images in containers” 中所寫,原生鏡像會(huì)提前預(yù)編譯您的應(yīng)用程序 (AOT)。這顯然消除了在運(yùn)行時(shí)開始編譯的需要,因此您的應(yīng)用程序幾乎可以立即啟動(dòng),并且內(nèi)存占用更少。這為即時(shí) (JIT) 編譯器基礎(chǔ)結(jié)構(gòu)、類元數(shù)據(jù)等節(jié)省了資源。

          除了快速啟動(dòng)之外,開發(fā)人員在應(yīng)用程序中使用 native images 還有不同的原因。這類部署具有云友好性,并且可以混淆代碼以提高安全性。

          圖 1是在談?wù)撔阅芎?GraalVM 可以運(yùn)行 Java 應(yīng)用程序的不同方式時(shí)經(jīng)常提到的圖片。你可以看到有很多軸,標(biāo)有人們?cè)谡f“更好的性能”時(shí)的意思。

          GraalVM 的性能改進(jìn)軸

          有時(shí),更好的性能取決于吞吐量以及一個(gè)服務(wù)實(shí)例可以處理多少個(gè)客戶端;有時(shí)它是關(guān)于盡可能快地提供單個(gè)響應(yīng)、內(nèi)存使用、啟動(dòng),甚至是部署的大小,因?yàn)樵谀承┣闆r下,這可能會(huì)有影響,例如冷啟動(dòng)性能。

          重要的是,通過一些相對(duì)簡(jiǎn)單的技巧,再加上使用先進(jìn)的 GraalVM Native Image 功能,您可以為您的應(yīng)用程序利用所有的這些優(yōu)勢(shì)。

          在本文中,我將向您展示如何為您的應(yīng)用程序充分利用GraalVM Native Image 技術(shù)。

          二、創(chuàng)建一個(gè)應(yīng)用

          假設(shè)您有一個(gè)簡(jiǎn)單的示例應(yīng)用程序,它是一個(gè)響應(yīng) HTTP 查詢并計(jì)算素?cái)?shù)的Micronaut 微服務(wù)。這是一個(gè)簡(jiǎn)單的單控制器應(yīng)用程序,它通過使用 JavaStream API和 CPU 方便地使用一些臨時(shí)對(duì)象生成垃圾收集器壓力來模擬業(yè)務(wù)邏輯,以非常低效地計(jì)算素?cái)?shù)序列:嘗試將所有數(shù)字作為因子,包括偶數(shù)大于 2。

          如果您安裝了 Micronaut 命令行工具,您可以通過以下方式創(chuàng)建此應(yīng)用程序。

          mn create-app org.shelajev.primes
          cd primes 
          cat <<'EOF' > src/main/java/org/shelajev/PrimesController.java 
          package org.shelajev;
          import io.micronaut.http.annotation.Controller;
          import io.micronaut.http.annotation.*;
          import java.util.stream.*;
          import java.util.*;
          @Controller("/primes")
          public class PrimesController {
              private Random r = new Random();
             
              @Get("/random/{upperbound}")
              public List<Long> random(int upperbound) {
                  int to = 2 + r.nextInt(upperbound - 2);
                  int from = 1 + r.nextInt(to - 1);
                  return primes(from, to);
              }
              public static boolean isPrime(long n) {
              return LongStream.rangeClosed(2, (long) Math.sqrt(n))
                      .allMatch(i -> n % i != 0);
              }
              public static List<Long> primes(long min, long max) {
              return LongStream.range(min, max)
                      .filter(PrimesController::isPrime)
                      .boxed()
                      .collect(Collectors.toList());
              }
          }
          EOF

          現(xiàn)在您有了示例應(yīng)用程序。您可以運(yùn)行它或立即將其構(gòu)建到 native 可執(zhí)行文件中。

          ./gradlew build
          ./gradlew nativeImage

          然后,您可以運(yùn)行該應(yīng)用程序。

          java -jar build/libs/primes-0.1-all.jar
          ./build/native-image/application

          要測(cè)試應(yīng)用程序,您可以手動(dòng)打開頁(yè)面或運(yùn)行curl命令,如下 所示,它將返回一個(gè)小于 100 的質(zhì)數(shù):

          curl http://localhost:8080/primes/random/100

          但是,為了幫助說明本文的后期階段,您應(yīng)該下載并安裝hey,這是一個(gè)簡(jiǎn)單的 HTTP 負(fù)載生成器,您將使用它來評(píng)估峰值性能。下載二進(jìn)制文件并將其放在$PATH上(如果 您不在 Linux 上,請(qǐng)獲取適當(dāng)?shù)亩M(jìn)制文件)。

          wget https://hey-release.s3.us-east-2.amazonaws.com/hey_linux_amd64
          chmod u+x hey_linux_amd64
          sudo mv hey_linux_amd64 /usr/local/bin/hey
          hey –version

          您可以通過運(yùn)行以下命令來驗(yàn)證它是否有效并熟悉它提供的輸出:

          hey -z 15s http://localhost:8080/primes/random/100

          輸出比此處包含的合理長(zhǎng)度要長(zhǎng),但它會(huì)打印延遲分布圖和摘要,如下所示:

          Summary:
            Total:    15.0021 secs
            Slowest:  0.1064 secs
            Fastest:  0.0001 secs
            Average:  0.0015 secs
            Requests/sec: 33703.8539
            Total data:   20062978 bytes
            Size/request: 20 bytes

          用于度量的最重要的部分是Requests/sec: 33703.8539行,它顯示了應(yīng)用程序的吞吐量。您將通過將應(yīng)用程序的堆大小限制為 512 MB 來進(jìn)行壓測(cè),而不是讓它無限增長(zhǎng)。默認(rèn)情況下,Native Image 將-Xmx選項(xiàng)設(shè)置為可用內(nèi)存的 80% 以限制堆大小,對(duì)于這個(gè)測(cè)試應(yīng)用程序來說,在功能強(qiáng)大的測(cè)試虛擬機(jī)上,這將是一種過度的做法。

          三、更好的內(nèi)存管理

          由于我在談?wù)搩?nèi)存,因此減少運(yùn)行時(shí)的內(nèi)存占用是一個(gè)重要指標(biāo),其中 Native Image 為使用通用 JDK 運(yùn)行應(yīng)用程序提供了改進(jìn)。

          這種節(jié)省主要是一次性的,因?yàn)槭褂?Native Image 構(gòu)建的可執(zhí)行文件包含應(yīng)用程序中已編譯的所有代碼和已分析的所有類。這允許您省去類元數(shù)據(jù)和 JIT 編譯器基礎(chǔ)結(jié)構(gòu)。

          但是,您的應(yīng)用程序所操作的數(shù)據(jù)集占用的內(nèi)存量相似,因?yàn)?JVM 和 native image 中的對(duì)象內(nèi)存布局相似。因此,如果一個(gè)應(yīng)用程序在內(nèi)存中保存了幾 GB 的數(shù)據(jù),那么本機(jī)映像將使用類似的量減去我上面提到的 200 MB 到 300 MB。

          Native Image 顯然包含一個(gè)支持應(yīng)用程序的運(yùn)行時(shí),該運(yùn)行時(shí)假定內(nèi)存是管理的,并且在需要時(shí)進(jìn)行垃圾回收。Native Image(包括垃圾回收)中使用的運(yùn)行時(shí)的實(shí)現(xiàn)來自 GraalVM 項(xiàng)目。

          上面提到的服務(wù)是用 Java 編寫的,由于在構(gòu)建應(yīng)用程序類的過程中,必須編譯依賴項(xiàng)和 JDK 類庫(kù)類,所以運(yùn)行時(shí)是與應(yīng)用程序一起編譯(串行垃圾收集器是一個(gè)簡(jiǎn)單的串行清除器,它針對(duì)吞吐量而不是最小化延遲進(jìn)行了優(yōu)化。)

          垃圾收集器公開了與 JDK 公開的用于指定堆大小的相同內(nèi)存配置選項(xiàng),例如:* -Xmx -最大堆 大小和* -Xmn -年輕代大小。如果您覺得需要查看幕后情況或?yàn)樘囟üぷ髫?fù)載微調(diào)垃圾收集器,那么-XX:+PrintGC-XX:+VerboseGC選項(xiàng)也可用。

          如果配置生成大小不是您的首選,那么可以使用多線程 G1 GC 垃圾收集器構(gòu)建 native image。G1 GC是 GraalVM Enterprise 中包含的一個(gè)面向性能的特性,它的配置非常簡(jiǎn)單。

          要啟用 G1 GC,請(qǐng)將--gc=G1參數(shù)傳遞給 Native Image 生成進(jìn)程。由于您正在使用 Micronaut 應(yīng)用程序,并依賴其 Gradle plugin 來配置和運(yùn)行 native image 構(gòu)建器,因此請(qǐng)?jiān)?code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #009688;">build.gradle文件中指定該選項(xiàng)。

          使用args行添加nativeImage配置,如下所示:

          n ativeImage {
            args("--gc=G1")
          }

          并再次構(gòu)建 native image。

          ./gradlew nativeImage

          調(diào)用此版本app-ee-g1可以更輕松地區(qū)分結(jié)果。在您運(yùn)行測(cè)試之前,我將向您展示一些其他有用的選項(xiàng),以提高本機(jī)圖像的性能。

          四、更好的整體吞吐量

          有幾個(gè)因素會(huì)影響應(yīng)用程序的吞吐量。當(dāng)然,其中一些主要因素是工作負(fù)載的性質(zhì)、代碼質(zhì)量、代碼處理的數(shù)據(jù)的數(shù)量和特征、輸入和輸出的延遲等等。但是,更好的運(yùn)行時(shí)或更好的編譯器可以顯著加快執(zhí)行速度。

          GraalVM Enterprise 附帶了一個(gè)更強(qiáng)大的編譯器——它可以創(chuàng)建一個(gè)執(zhí)行概要文件,類似于 JIT 編譯器在應(yīng)用程序運(yùn)行時(shí)所做的工作。編譯器可以使用此配置文件在 AOT 編譯期間生成所謂的概要文件引導(dǎo)優(yōu)化 (PGO)。PGO 可以使生成的可執(zhí)行文件的吞吐量更接近預(yù)熱的 JIT 數(shù)字。

          這里需要注意的一點(diǎn)是 JIT 編譯器最好的特性是在運(yùn)行時(shí)運(yùn)行,這使得它可用的數(shù)據(jù)始終與當(dāng)前工作負(fù)載相關(guān)。如果在運(yùn)行與生產(chǎn)中類似的工作負(fù)載時(shí)收集摘要文件,那么使用概要文件的 GraalVM AOT 編譯效果最好。這通常很容易通過設(shè)計(jì)良好的微服務(wù)實(shí)現(xiàn)。下面是示例應(yīng)用程序的實(shí)現(xiàn)方法。

          首先,構(gòu)建一個(gè)檢測(cè)二進(jìn)制文件,用于收集 PGO 的摘要文件。啟用該選項(xiàng)的選項(xiàng)是--pgo-instrument。您可以將其添加到build.gradle配置中,并使用./gradlew nativeImage正常構(gòu)建 image,如下所示:

          native Image {
            args("--gc=G1")
            args("--pgo-instrument")
          }

          現(xiàn)在您可以構(gòu)建應(yīng)用程序并運(yùn)行相同的負(fù)載生成工具。

          ./build/native-image/application

          然后運(yùn)行以下命令:

          hey -z 15s http://localhost:8080/primes/random/100

          當(dāng)它停止時(shí),應(yīng)用程序?qū)⒃诋?dāng)前目錄中創(chuàng)建default.iprof文件(除非配置沒有開啟)。現(xiàn)在您可以使用--pgo選項(xiàng)構(gòu)建最終 image,同時(shí)提供正確的路徑。請(qǐng)注意,該路徑將向上移動(dòng)兩個(gè)目錄:Micronaut 在build/native-image目錄中構(gòu)建本機(jī)映像,您將從項(xiàng)目的主 目錄中執(zhí)行檢測(cè)的二進(jìn)制文件,如下所示:

          nativeImage {
            args("--gc=G1")
            args("--pgo=../../default.iprof")
          }

          構(gòu)建完成并存儲(chǔ)具有描述性app-ee-pgo名稱的二進(jìn)制文件后,您就可以觀察結(jié)果了。

          五、更小的二進(jìn)制文件

          在對(duì)性能數(shù)據(jù)進(jìn)行最有趣的比較之前,先看看可執(zhí)行文件的大小。二進(jìn)制文件很大。你可以把它們變小。

          以下是這個(gè)簡(jiǎn)單應(yīng)用程序中二進(jìn)制文件的大小,沒有對(duì)大小進(jìn)行任何優(yōu)化。

          $ ls -lah app*
          -rwxrwxr-x. 1 opc opc  58M May  6 20:41 app-ce
          -rwxrwxr-x. 1 opc opc  73M May  6 21:14 app-ee
          -rwxrwxr-x. 1 opc opc  99M May  6 21:25 app-ee-g1
          -rwxrwxr-x. 1 opc opc  80M May  6 21:47 app-ee-pgo

          我沒有列出用于收集 PGO 配置文件的二進(jìn)制文件,因?yàn)樗鼘?shí)際上并不打算在生產(chǎn)中分發(fā)和使用,但為了完整起見,它大約為 250 MB。二進(jìn)制文件由兩個(gè)主要部分組成:應(yīng)用程序的預(yù)編譯代碼和在構(gòu)建時(shí)類初始化期間創(chuàng)建的數(shù)據(jù),稱為image heap。為了有效地利用 native images,理解兩者同樣重要。

          代碼部分。這部分最容易掌握。這部分包含需要包含在圖像中的所有類和方法,因?yàn)殪o態(tài)分析發(fā)現(xiàn)了它們的可能代碼路徑,或者它們的包含是使用顯式配置預(yù)先配置的。

          代碼部分包括您的類、它們的依賴項(xiàng)、依賴項(xiàng)的依賴項(xiàng)等等,直到 JDK 類庫(kù)類和在構(gòu)建時(shí)生成的類。換句話說,所有的 Java 字節(jié)碼都將在生成的可執(zhí)行文件中執(zhí)行。

          注意,代碼部分不包括處理字節(jié)碼加載、驗(yàn)證、解釋或 JIT 編譯的基礎(chǔ)結(jié)構(gòu)。因此,很自然地,如果沒有提前編譯某些內(nèi)容,它將在運(yùn)行時(shí)不可用和可執(zhí)行。

          Image heap。這部分是很多開發(fā)者比較陌生的概念。將 native image 構(gòu)建過程視為運(yùn)行您的應(yīng)用程序一段時(shí)間,初始化一些必要的類及其數(shù)據(jù),并保存應(yīng)用程序的狀態(tài)以備將來使用。然后,當(dāng)您在測(cè)試或生產(chǎn)中運(yùn)行可執(zhí)行文件時(shí),已準(zhǔn)備好使用初始化狀態(tài)并且應(yīng)用程序啟動(dòng)是即時(shí)的。這個(gè)狀態(tài)顯然需要寫在某處,這就是存儲(chǔ)在 image heap 中的內(nèi)容。

          在 native image 構(gòu)建期間,您可以使用報(bào)告選項(xiàng)(-H:+DashboardAll) 觀察應(yīng)用程序中的類和包對(duì)最終可執(zhí)行文件大小的貢獻(xiàn)空間。有了這些信息,您就可以重構(gòu)應(yīng)用程序,以消除可能使 image heap 大于所需的代碼路徑。

          您還可以犧牲一點(diǎn)啟動(dòng)速度來?yè)Q取更好的打包(體驗(yàn)),例如使用UPX,它代表可執(zhí)行文件的終極打包器。UPX 可以壓縮二進(jìn)制文件,而且效果出奇地好,通常生成的二進(jìn)制文件的大小是原始文件的 30% 左右。當(dāng) GraalVM 團(tuán)隊(duì)研究使用 UPX時(shí),我們發(fā)現(xiàn)7左右的中等高的打包級(jí)別是一個(gè)很好的默認(rèn)值。

          以下是將 UPX 應(yīng)用于此處的示例二進(jìn)制文件之一的示例結(jié)果:

          upx -7 -k app-ee-pgo
                                 Ultimate Packer for eXecutables
                                    Copyright (C) 1996 - 2020
          UPX 3.96        Markus Oberhumer, Laszlo Molnar & John Reiser   Jan 23rd 2020
                  File size         Ratio      Format      Name
             --------------------   ------   -----------   -----------
            83267776 ->  24043764   28.88%   linux/amd64   app-ee-pgo
          Packed 1 file.

          二進(jìn)制文件大小從 80 MB 減少到 23 MB,并且該應(yīng)用程序的響應(yīng)速度仍然非常快。

          $ ./app-ee-pgo
           __  __ _                                  _
          |  \/  (_) ___ _ __ ___  _ __   __ _ _   _| |_
          | |\/| | |/ __| '__/ _ \| '_ \ / _` | | | | __|
          | |  | | | (__| | | (_) | | | | (_| | |_| | |_
          |_|  |_|_|\___|_|  \___/|_| |_|\__,_|\__,_|\__|
            Micronaut (v2.5.1)
          20:33:44.838 [main] INFO  i.m.context.env.DefaultEnvironment - Established active environments: [oraclecloud, cloud]
          20:33:44.852 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 20ms. Server Running: http://ol8-demo:8080

          這些是制作應(yīng)用程序小型 native images 的一些方法。當(dāng)然,您可以分別通過分析報(bào)告和更改代碼來進(jìn)一步優(yōu)化,但是這種類型的優(yōu)化很快就進(jìn)入了收益遞減的領(lǐng)域。

          六、Native Image 能帶你走多遠(yuǎn)?

          我已經(jīng)向您展示了一些優(yōu)化 Native Image 生成可執(zhí)行文件性能的不同方法:從使用更復(fù)雜和自適應(yīng)的 G1 GC,因此您不必手動(dòng)調(diào)整內(nèi)存設(shè)置,到啟用配置文件引導(dǎo)優(yōu)化,以更有效地打包可執(zhí)行文件以減少磁盤或容器占用空間。

          您現(xiàn)在應(yīng)該運(yùn)行示例負(fù)載生成腳本以查看這些優(yōu)化是否有任何不同。對(duì)于本文,我在堆限制為 512 MB 的情況下運(yùn)行了以下 3 次 15 秒的測(cè)試:

          • GraalVM Enterprise 的默認(rèn)本機(jī)映像和 GC:* app-ee
          • 使用同樣的 G1 GC:* app-ee-g1
          • 再加上 PGO:* app-ee-pgo
          ./app-ee -Xmx512m
          Summary:
            Total:    15.0023 secs
            Slowest:  0.1304 secs
            Fastest:  0.0001 secs
            Average:  0.0010 secs
            Requests/sec: 49770.7845
          ./app-ee-g1 -Xmx512m
          Summary:
            Total:    15.0029 secs
            Slowest:  0.1388 secs
            Fastest:  0.0001 secs
            Average:  0.0010 secs
            Requests/sec: 51690.8255
          ./app-ee-pgo -Xmx512m
          Summary:
            Total:    15.0023 secs
            Slowest:  0.1193 secs
            Fastest:  0.0001 secs
            Average:  0.0007 secs
            Requests/sec: 73391.9314

          正如您所見,初始開箱即用的構(gòu)建與使用 G1 GC 和 PGO 的構(gòu)建之間的差異是驚人的。只是為了好玩,我在 OpenJDK 上運(yùn)行的同一個(gè)應(yīng)用程序上運(yùn)行了相同的加載。我使用了基于 JDK 11 的 GraalVM,精確的版本如下。

          java -version
          java version "11.0.11" 2021-04-20 LTS
          Java(TM) SE Runtime Environment GraalVM EE 21.1.0 (build 11.0.11+9-LTS-jvmci-21.1-b05)
          Java HotSpot(TM) 64-Bit Server VM GraalVM EE 21.1.0 (build 11.0.11+9-LTS-jvmci-21.1-b05, mixed mode, sharing)

          為了進(jìn)行比較,我從 SDKMAN 中選擇了構(gòu)建在相同版本 11.0.11 上的任意 JDK 發(fā)行版!以下是結(jié)果。

          java -Xmx512m -jar build/libs/primes-0.1-all.jar
          Summary:
            Total:    15.0019 secs
            Slowest:  0.4774 secs
            Fastest:  0.0001 secs
            Average:  0.0008 secs
            Requests/sec: 62991.1439

          在這個(gè)測(cè)試中,性能最好的 native image 比 OpenJDK 快 16%。當(dāng)然,這對(duì)于依賴 JIT 編譯并需要預(yù)熱的運(yùn)行時(shí)來說并不是一個(gè)完全公平的測(cè)試。再說一次,15 秒和處理一百萬個(gè)請(qǐng)求是相當(dāng)長(zhǎng)的時(shí)間,尤其是在具有大量 CPU 容量的強(qiáng)大機(jī)器上,因此 JIT 編譯器可以與應(yīng)用程序代碼并行工作。

          在任何情況下,您都可以看到 native image 的性能可以與使用 JIT 編譯器運(yùn)行您的應(yīng)用程序相媲美,同時(shí)可以獲得更好的啟動(dòng)性能,并且更適合于受限環(huán)境或微服務(wù)。

          七、結(jié)論

          本文介紹了在不更改任何代碼的情況下提高 native images 性能的不同方法。使用自適應(yīng) G1 GC,應(yīng)用 profile-guided  優(yōu)化,并使用 UPX 打包可執(zhí)行文件,生成了一個(gè)非常高效的微服務(wù),其大小約為 20 MB,在 20 毫秒內(nèi)啟動(dòng),并且在服務(wù)的前 100 萬個(gè)請(qǐng)求上優(yōu)于 OpenJDK。

          GraalVM Native Image 是一項(xiàng)非常令人興奮的技術(shù),適用于云環(huán)境中的 Java 工作負(fù)載。希望本文向您介紹了在不更改應(yīng)用程序代碼的情況下更有效地使用 Native Image 的一些方法。

          譯者說:

          大家好,我是 如夢(mèng)技術(shù)春哥Mica 開源作者)感謝深夜還一起參與翻譯和校對(duì)的張亞東JustAuth 開源作者)同學(xué)。我們已經(jīng)輸出和翻譯了多篇 GraalVM 和 Spring Native 的文章:

          翻譯不易,請(qǐng)幫忙分享給更多的同學(xué),謝謝!!!


          鏈接

          1.  GraalVM: Native images in containers: https://blogs.oracle.com/javamagazine/graalvm-native-images-in-containers

          2. 簡(jiǎn)單的 HTTP 負(fù)載生成器: https://github.com/rakyll/hey

          3. G1 GC: https://www.graalvm.org/reference-manual/native-image/MemoryManagement/


          瀏覽 296
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  午夜精品久久99热蜜桃剧情介绍 | 亚洲视频在线看 | 国产精品视频一区二区三区 | 黄片操逼操逼 | 韩国精品无码 |