Kotlin Gradle 外掛程式中的編譯與快取
在此頁面中,您可以了解以下主題:
- 增量編譯
- Gradle 組建快取支援
- Gradle 組態快取支援
- Kotlin daemon 以及如何在 Gradle 中使用它
- 回退至先前的編譯器
- 定義 Kotlin 編譯器執行策略
- Kotlin 編譯器回退策略
- 嘗試最新的語言版本
- 組建報告
增量編譯
Kotlin Gradle 外掛程式支援增量編譯,這在 Kotlin/JVM 和 Kotlin/JS 專案中是預設啟用的。 增量編譯會追蹤 classpath 中檔案在兩次組建之間的變更,以便僅編譯受這些變更影響的檔案。 此方法可與 Gradle 的組建快取配合使用,並支援 編譯規避 (compilation avoidance)。
對於 Kotlin/JVM,增量編譯依賴於 classpath 快照,這些快照會擷取模組的 API 結構,以確定何時需要重新編譯。 為了優化整體管線,Kotlin 編譯器使用兩種類型的 classpath 快照:
- 細粒度快照 (Fine-grained snapshots): 包含關於類別成員(例如屬性或函式)的詳細資訊。 當偵測到成員層級的變更時,Kotlin 編譯器僅重新編譯相依於該修改成員的類別。 為了維持效能,Kotlin Gradle 外掛程式會針對 Gradle 快取中的
.jar檔案建立粗粒度快照。 - 粗粒度快照 (Coarse-grained snapshots): 僅包含類別 ABI 雜湊。 當 ABI 的某部分發生變化時,Kotlin 編譯器會重新編譯所有相依於該變更類別的類別。 這對於不常更動的類別(例如外部程式庫)非常有用。
Kotlin/JS 專案使用基於歷程記錄檔案的不同增量編譯方法。
有幾種方式可以停用增量編譯:
針對 Kotlin/JVM,設定
kotlin.incremental=false。針對 Kotlin/JS 專案,設定
kotlin.incremental.js=false。使用
-Pkotlin.incremental=false或-Pkotlin.incremental.js=false作為命令列參數。該參數應新增至後續的每次組建中。
當您停用增量編譯時,增量快取會在組建後失效。第一次組建絕對不會是增量的。
有時增量編譯的問題會在失敗發生後的幾個回合才顯現出來。請使用 組建報告 來追蹤變更與編譯的歷程記錄。這可以幫助您提供可重現的錯誤報告。
若要進一步了解我們目前的增量編譯方法及其與先前方法的比較,請參閱我們的 部落格文章。
Gradle 組建快取支援
Kotlin 外掛程式使用 Gradle 組建快取,它會儲存組建輸出,以便在未來的組建中重複使用。
若要停用所有 Kotlin 任務的快取,請將系統屬性 kotlin.caching.enabled 設定為 false(執行組建時帶上引數 -Dkotlin.caching.enabled=false)。
Gradle 組態快取支援
Kotlin 外掛程式使用 Gradle 組態快取,透過重複使用組態階段的結果來加速後續組建的過程。
請參閱 Gradle 文件 以了解如何啟用組態快取。在您啟用此功能後,Kotlin Gradle 外掛程式會自動開始使用它。
Kotlin daemon 以及如何在 Gradle 中使用它
- 與 Gradle daemon 一起執行以編譯專案。
- 當您使用 IntelliJ IDEA 內建組建系統編譯專案時,會與 Gradle daemon 分開執行。
Kotlin daemon 會在 Gradle 執行階段中,當其中一個 Kotlin 編譯任務開始編譯原始碼時啟動。 Kotlin daemon 會隨 Gradle daemon 一起停止,或在連續兩小時沒有 Kotlin 編譯任務的閒置後停止。
Kotlin daemon 使用與 Gradle daemon 相同的 JDK。
設定 Kotlin daemon 的 JVM 引數
以下每種設定引數的方式都會覆蓋其之前的設定:
Gradle daemon 引數繼承
預設情況下,Kotlin daemon 會從 Gradle daemon 繼承一組特定的引數,但會使用直接為 Kotlin daemon 指定的任何 JVM 引數將其覆寫。例如,如果您在 gradle.properties 檔案中新增以下 JVM 引數:
org.gradle.jvmargs=-Xmx1500m -Xms500m -XX:MaxMetaspaceSize=1g這些引數隨後會被新增到 Kotlin daemon 的 JVM 引數中:
-Xmx1500m -XX:ReservedCodeCacheSize=320m -XX:MaxMetaspaceSize=1g -XX:UseParallelGC -ea -XX:+UseCodeCacheFlushing -XX:+HeapDumpOnOutOfMemoryError -Djava.awt.headless=true -Djava.rmi.server.hostname=127.0.0.1 --add-exports=java.base/sun.nio.ch=ALL-UNNAMED若要進一步了解 Kotlin daemon 處理 JVM 引數的預設行為,請參閱 Kotlin daemon 處理 JVM 引數的行為。
kotlin.daemon.jvm.options 系統屬性
如果 Gradle daemon 的 JVM 引數具有 kotlin.daemon.jvm.options 系統屬性 – 請在 gradle.properties 檔案中使用它:
org.gradle.jvmargs=-Dkotlin.daemon.jvm.options=-Xmx1500m,Xms500m傳遞引數時,請遵循以下規則:
- 僅 在引數
Xmx、XX:MaxMetaspaceSize和XX:ReservedCodeCacheSize之前使用減號-。 - 使用逗號 (
,) 分隔引數,中間 不加 空格。空格之後的引數將用於 Gradle daemon,而非 Kotlin daemon。
如果滿足以下所有條件,Gradle 會忽略這些屬性:
- Gradle 正在使用 JDK 1.9 或更高版本。
- Gradle 版本介於 7.0 到 7.1.1 之間(含)。
- Gradle 正在編譯 Kotlin DSL 指令碼。
- Kotlin daemon 尚未執行。
若要克服此問題,請將 Gradle 升級至 7.2 版(或更高版本),或使用
kotlin.daemon.jvmargs屬性 – 請參閱下一節。
kotlin.daemon.jvmargs 屬性
您可以在 gradle.properties 檔案中新增 kotlin.daemon.jvmargs 屬性:
kotlin.daemon.jvmargs=-Xmx1500m -Xms500m請注意,如果您未在此處或 Gradle 的 JVM 引數中指定 ReservedCodeCacheSize 引數,Kotlin Gradle 外掛程式將套用預設值 320m:
-Xmx1500m -XX:ReservedCodeCacheSize=320m -Xms500mkotlin 擴充套件
您可以在 kotlin 擴充套件中指定引數:
kotlin {
kotlinDaemonJvmArgs = listOf("-Xmx486m", "-Xms256m", "-XX:+UseParallelGC")
}kotlin {
kotlinDaemonJvmArgs = ["-Xmx486m", "-Xms256m", "-XX:+UseParallelGC"]
}特定任務定義
您可以為特定任務指定引數:
tasks.withType<CompileUsingKotlinDaemon>().configureEach {
kotlinDaemonJvmArguments.set(listOf("-Xmx486m", "-Xms256m", "-XX:+UseParallelGC"))
}tasks.withType(CompileUsingKotlinDaemon).configureEach { task ->
task.kotlinDaemonJvmArguments = ["-Xmx1g", "-Xms512m"]
}在這種情況下,一個新的 Kotlin daemon 執行個體可能會在任務執行時啟動。進一步了解 Kotlin daemon 處理 JVM 引數的行為。
Kotlin daemon 處理 JVM 引數的行為
設定 Kotlin daemon 的 JVM 引數時,請注意:
- 當不同的子專案或任務具有不同的 JVM 引數組合時,預期會同時執行多個 Kotlin daemon 執行個體。
- 只有當 Gradle 執行相關編譯任務,且現有的 Kotlin daemon 沒有相同的 JVM 引數組合時,才會啟動新的 Kotlin daemon 執行個體。 想像您的專案有很多子專案。大多數子專案只需要一定的堆積記憶體給 Kotlin daemon,但有一個模組需要非常多(儘管它很少被編譯)。 在這種情況下,您應該為該模組提供一組不同的 JVM 引數,這樣只有在開發人員接觸該特定模組時,才會啟動具有較大堆積大小的 Kotlin daemon。
如果您已經有一個正在執行的 Kotlin daemon 具有足夠的堆積大小來處理編譯請求,即使其他請求的 JVM 引數不同,也會重複使用該 daemon 而非啟動新的。
如果未指定以下引數,Kotlin daemon 會從 Gradle daemon 繼承:
-Xmx-XX:MaxMetaspaceSize-XX:ReservedCodeCacheSize。如果未指定或繼承,預設值為320m。
Kotlin daemon 具有以下預設 JVM 引數:
-XX:UseParallelGC。僅當未指定其他垃圾收集器時,才會套用此引數。-ea-XX:+UseCodeCacheFlushing-Djava.awt.headless=true-D{java.servername.property}={localhostip}--add-exports=java.base/sun.nio.ch=ALL-UNNAMED。此引數僅套用於 JDK 16 或更高版本。
Kotlin daemon 的預設 JVM 引數列表可能會隨版本而異。您可以使用像 VisualVM 這樣的工具來檢查執行中 JVM 程序(如 Kotlin daemon)的實際設定。
回退至先前的編譯器
從 Kotlin 2.0.0 開始,預設使用 K2 編譯器。
若要在 Kotlin 2.0.0 及更高版本中使用先前的編譯器,請執行以下任一操作:
在您的
build.gradle.kts檔案中,將您的語言版本設定為1.9。或
使用以下編譯器選項:
-language-version 1.9。
若要進一步了解 K2 編譯器的優點,請參閱 K2 編譯器遷移指南。
嘗試最新的語言版本
從 Kotlin 2.0.0 開始,若要嘗試最新的語言版本,請在 gradle.properties 檔案中設定 kotlin.experimental.tryNext 屬性。 當您使用此屬性時,Kotlin Gradle 外掛程式會將語言版本增加到比您 Kotlin 版本預設值高一級的版本。 例如,在 Kotlin 2.0.0 中,預設語言版本為 2.0,因此該屬性會將語言版本配置為 2.1。
或者,您可以執行以下指令:
./gradlew assemble -Pkotlin.experimental.tryNext=true在 組建報告 中,您可以找到用於編譯每個任務的語言版本。
組建報告
組建報告包含不同編譯階段的持續時間,以及編譯無法進行增量編譯的任何原因。 當編譯時間太長或同一個專案的編譯時間出現差異時,請使用組建報告來調查效能問題。
Kotlin 組建報告比 Gradle build scans 能更有效地幫助您調查組建效能問題,因為後者的細微性單位是單一 Gradle 任務。
分析執行時間較長的編譯報告可以幫助您解決以下兩個常見案例:
- 組建不是增量的。分析原因並修正底層問題。
- 組建是增量的,但花費了太多時間。嘗試重新組織原始檔 — 拆分大型檔案、將不同的類別儲存在不同的檔案中、重構大型類別、在不同的檔案中宣告頂層函式等等。
組建報告還會顯示專案中使用的 Kotlin 版本。此外,從 Kotlin 1.9.0 開始,您可以在 Gradle build scans 中看到用於編譯程式碼的編譯器。
了解 如何閱讀組建報告 以及 JetBrains 如何使用組建報告。
啟用組建報告
若要啟用組建報告,請在 gradle.properties 中宣告組建報告輸出的儲存位置:
kotlin.build.report.output=file輸出可使用以下值及其組合:
| 選項 | 描述 |
|---|---|
file | 將組建報告以人類可讀的格式儲存到本機檔案。預設路徑為 ${project_folder}/build/reports/kotlin-build/${project_name}-timestamp.txt |
single_file | 將組建報告以物件格式儲存到指定的本機檔案。 |
build_scan | 將組建報告儲存在 build scan 的 custom values 區段。請注意,Gradle Enterprise 外掛程式限制了自訂值的數量及其長度。在大型專案中,某些值可能會遺失。 |
http | 使用 HTTP(S) 發布組建報告。POST 方法以 JSON 格式傳送指標。您可以在 Kotlin 儲存庫 中查看傳送資料的目前版本。您可以在 這篇部落格文章 中找到 HTTP 端點的範例。 |
json | 將組建報告以 JSON 格式儲存到本機檔案。在 kotlin.build.report.json.directory 中設定組建報告的位置(見下文)。預設名稱為 ${project_name}-build-<date-time>-<index>.json。 |
以下是 kotlin.build.report 的可用選項列表:
# 必要的輸出。允許任何組合
kotlin.build.report.output=file,single_file,http,build_scan,json
# 如果使用 single_file 輸出則為必填。報告存放位置
# 用於取代已棄用的 `kotlin.internal.single.build.metrics.file` 屬性
kotlin.build.report.single_file=some_filename
# 如果使用 json 輸出則為必填。報告存放位置
kotlin.build.report.json.directory=my/directory/path
# 選填。基於檔案之報告的輸出目錄。預設值:build/reports/kotlin-build/
kotlin.build.report.file.output_dir=kotlin-reports
# 選填。用於標記組建報告的標籤(例如,偵錯參數)
kotlin.build.report.label=some_label僅適用於 HTTP 的選項:
# 必填。發布基於 HTTP(S) 報告的位置
kotlin.build.report.http.url=http://127.0.0.1:8080
# 選填。如果 HTTP 端點需要驗證,則提供使用者與密碼
kotlin.build.report.http.user=someUser
kotlin.build.report.http.password=somePassword
# 選填。將組建的 Git 分支名稱新增至組建報告
kotlin.build.report.http.include_git_branch.name=true|false
# 選填。將編譯器引數新增至組建報告
# 如果專案包含許多模組,報告中的編譯器引數可能會非常沈重且沒什麼幫助
kotlin.build.report.include_compiler_arguments=true|false自訂值限制
為了收集組建掃描的統計數據,Kotlin 組建報告使用 Gradle 的自訂值 (custom values)。 您和不同的 Gradle 外掛程式都可以將資料寫入自訂值。自訂值的數量有限制。 請在 Build scan 外掛程式文件 中查看目前的自訂值數量上限。
如果您有一個大型專案,此類自訂值的數量可能會相當多。如果此數量超過限制,您可能會在日誌中看到以下訊息:
Maximum number of custom values (1,000) exceeded若要減少 Kotlin 外掛程式產生的自訂值數量,您可以在 gradle.properties 中使用以下屬性:
kotlin.build.report.build_scan.custom_values_limit=500關閉專案與系統屬性的收集
HTTP 組建統計日誌可能包含某些專案與系統屬性。這些屬性會改變組建的行為,因此在組建統計中記錄它們很有用。 但這些屬性可能儲存敏感資料,例如密碼或專案的完整路徑。
您可以透過在 gradle.properties 中新增 kotlin.build.report.http.verbose_environment 屬性來停用這些統計資料的收集。
JetBrains 不會收集這些統計資料。您可以選擇 儲存報告的位置。
下一步?
進一步了解:
