守护进程是一个计算机程序,它作为后台进程运行,而不是直接受交互用户的控制。

Gradle 在 Java 虚拟机 (JVM) 上运行,并使用多个具有非平凡初始化时间的支撑库。启动可能很慢。Gradle 守护进程解决了这个问题。

Gradle 守护进程是一个长期运行的后台进程,它可以减少运行构建所需的时间。

Gradle 守护进程通过以下方式减少构建时间:

  • 跨构建缓存项目信息

  • 在后台运行,因此每次 Gradle 构建都不必等待 JVM 启动

  • 受益于 JVM 中的持续运行时优化

  • 监视文件系统,以便在运行构建之前准确计算出需要重建的内容

理解守护进程

Gradle JVM 客户端将守护进程构建信息(例如命令行参数、项目目录和环境变量)发送给守护进程,以便它可以运行构建。守护进程负责解析依赖项、执行构建脚本、创建和运行任务;完成后,它会将输出发送给客户端。客户端和守护进程之间的通信通过本地套接字连接进行。

守护进程使用 JVM 的默认最小堆大小。

如果请求的构建环境未指定最大堆大小,则守护进程最多使用 512MB 的堆。512MB 对于大多数构建来说是足够的。具有数百个子项目、配置和源代码的较大构建可能会受益于更大的堆大小。

检查守护进程状态

要获取正在运行的守护进程及其状态的列表,请使用 --status 命令

$ gradle --status
   PID STATUS   INFO
 28486 IDLE     7.5
 34247 BUSY     7.5

目前,给定的 Gradle 版本只能连接到相同版本的守护进程。这意味着状态输出仅显示与当前项目运行相同 Gradle 版本的守护进程。

查找守护进程

如果您已安装 Java 开发工具包 (JDK),则可以使用 jps 命令查看实时守护进程。

$ jps
33920 Jps
27171 GradleDaemon
22792

实时守护进程以名称 GradleDaemon 显示。由于此命令使用 JDK,因此您可以查看运行任何 Gradle 版本的守护进程。

启用守护进程

自 Gradle 3.0 以来,Gradle 默认启用守护进程。如果您的项目未使用守护进程,则可以使用 --daemon 标志在运行构建时为单个构建启用它

$ gradle <task> --daemon

此标志会覆盖禁用项目或用户 gradle.properties 文件中守护进程的任何设置。

要在较旧的 Gradle 版本中默认启用守护进程,请将以下设置添加到项目根目录或 Gradle 用户主目录(GRADLE_USER_HOME

gradle.properties
org.gradle.daemon=true

禁用守护进程

您可以通过多种方式禁用守护进程,但有一些重要的注意事项

单次使用守护进程

如果客户端进程的 JVM 参数与构建所需的参数不匹配,则会创建一个单次使用守护进程(一次性 JVM)。这意味着构建需要守护进程,因此它会被创建、使用,然后在构建结束时停止。

无守护进程

如果 JAVA_OPTSGRADLE_OPTSorg.gradle.jvmargs 匹配,则根本不会使用守护进程,因为构建发生在客户端 JVM 中。

为构建禁用

要为单个构建禁用守护进程,请在运行构建时传递 --no-daemon 标志

$ gradle <task> --no-daemon

此标志会覆盖启用项目中守护进程的任何设置,包括 gradle.properties 文件。

为项目禁用

要为项目的所有构建禁用守护进程,请将 org.gradle.daemon=false 添加到项目根目录中的 gradle.properties 文件中。

为用户禁用

在 Windows 上,此命令为当前用户禁用守护进程

(if not exist "%USERPROFILE%/.gradle" mkdir "%USERPROFILE%/.gradle") && (echo. >> "%USERPROFILE%/.gradle/gradle.properties" && echo org.gradle.daemon=false >> "%USERPROFILE%/.gradle/gradle.properties")

在类 UNIX 操作系统上,以下 Bash shell 命令为当前用户禁用守护进程

mkdir -p ~/.gradle && echo "org.gradle.daemon=false" >> ~/.gradle/gradle.properties

全局禁用

有两种推荐的方法可以在整个环境中全局禁用守护进程

  • org.gradle.daemon=false 添加到 $GRADLE_USER_HOME/gradle.properties` 文件

  • 将标志 -Dorg.gradle.daemon=false 添加到 GRADLE_OPTS 环境变量

不要忘记确保您的 JVM 参数和 GRADLE_OPTS / JAVA_OPTS 匹配,如果您想完全禁用守护进程,而不是仅仅调用单次使用的守护进程。

停止守护进程

在排除故障或调试故障时,停止守护进程可能很有用。

在以下任何情况下,守护进程都会自动停止

  • 可用系统内存不足

  • 守护进程已空闲 3 小时

要停止正在运行的守护进程进程,请使用以下命令

$ gradle --stop

这将终止使用与执行命令的 Gradle 版本相同的版本启动的所有守护进程进程。

您也可以使用操作系统手动终止守护进程。要查找所有守护进程的 PID(无论 Gradle 版本如何),请参阅 查找守护进程

配置守护进程 JVM

守护进程 JVM 发现和标准是 孵化中 的功能,并且在未来的版本中可能会发生更改。

默认情况下,Gradle 守护进程使用启动构建的同一 JVM 安装运行。Gradle 默认使用当前 shell 路径和 JAVA_HOME 环境变量来查找可用的 JVM。

或者,可以使用 org.gradle.java.home Gradle 属性或通过 Tooling API 以编程方式为构建指定不同的 JVM 安装。

工具链功能 的基础上,您现在可以使用声明性标准来指定构建的 JVM 要求。

如果提供了守护进程 JVM 标准配置,则它优先于 JAVA_HOMEorg.gradle.java.home

守护进程 JVM 标准

守护进程 JVM 标准updateDaemonJvm 任务控制,类似于 wrapper 任务 更新 wrapper 属性文件的方式。

此过程需要配置 工具链下载仓库。有关详细信息,请参阅下面的 配置供应 URL

当任务运行时,它会在 gradle/gradle-daemon-jvm.properties 文件中创建或更新标准。

要配置生成,您可以使用命令行选项

$ ./gradlew updateDaemonJvm --jvm-version=17

或在根项目的构建脚本中配置任务

build.gradle.kts
tasks.named<UpdateDaemonJvm>("updateDaemonJvm") {
    languageVersion = JavaLanguageVersion.of(17)
}
build.gradle
tasks.named("updateDaemonJvm") {
    languageVersion = JavaLanguageVersion.of(17)
}

然后运行任务

$ ./gradlew updateDaemonJvm

这两个操作都将生成如下文件

gradle/gradle-daemon-jvm.properties
#This file is generated by updateDaemonJvm
toolchainUrl.FREE_BSD.AARCH64=https\://example.com/...
toolchainUrl.FREE_BSD.X86_64=https\://example.com/...
toolchainUrl.LINUX.AARCH64=https\://example.com/...
toolchainUrl.LINUX.X86_64=https\://example.com/...
toolchainUrl.MAC_OS.AARCH64=https\://example.com/...
toolchainUrl.MAC_OS.X86_64=https\://example.com/...
toolchainUrl.UNIX.AARCH64=https\://example.com/...
toolchainUrl.UNIX.X86_64=https\://example.com/...
toolchainUrl.WINDOWS.X86_64=https\://example.com/...
toolchainVersion=17

如果您在没有任何参数的情况下运行 updateDaemonJvm 任务,并且属性文件不存在,则将使用守护进程当前使用的 JVM 版本。

在下次执行构建时,Gradle 客户端将使用此文件来查找兼容的 JVM 安装并使用它启动守护进程。

与 wrapper 类似,生成的 gradle-daemon-jvm.properties 文件应签入版本控制。这确保了运行构建的任何开发人员或 CI 服务器都将使用相同的 JVM 版本。

指定 JVM 供应商

JVM 供应商(如 JVM 版本)可以用作选择兼容的 JVM 安装以进行构建的标准。如果未指定供应商,Gradle 会认为所有供应商都兼容。

默认情况下,运行 updateDaemonJvm 以创建 gradle-daemon-jvm.properties 文件不会生成 JVM 供应商标准。要指定供应商,请在构建脚本中配置它,使用 与 Java 工具链规范相同的语法,或在命令行中传递它

$ ./gradlew updateDaemonJvm --jvm-version=17 --jvm-vendor=adoptium

已识别供应商列表

已知供应商 可接受的字符串 toolchainVendor

Adoptium / Eclipse Temurin

adoptium, temurin, eclipse foundation

ADOPTIUM

AdoptOpenJDK

adoptopenjdk, aoj

ADOPTOPENJDK

Amazon Corretto

amazon, corretto

AMAZON

Apple

apple

APPLE

Azul Zulu

azul, zulu

AZUL

BellSoft

bellsoft, liberica

BELLSOFT

GraalVM

graalvm, graal vm

GRAAL_VM

Hewlett Packard

hp, hewlett

HEWLETT_PACKARD

IBM

ibm, semeru, international business machines corporation

IBM

JetBrains

jetbrains, jbr

JETBRAINS

Microsoft

microsoft

MICROSOFT

Oracle

oracle

ORACLE

SAP

sap

SAP

Tencent

tencent, kona

TENCENT

某些供应商将从多组字符中识别出来。所有供应商字符串均不区分大小写。您可以通过运行 ./gradlew help --task updateDaemonJvm 查看已识别供应商的列表。

如果指定的供应商不是已识别的等效项之一,Gradle 将完全匹配它。例如,“MyCustomJVM”将需要完全匹配供应商名称。

自动检测和自动供应

守护进程 JVM 使用与 项目 JVM 工具链 相同的逻辑进行自动检测

对于自动供应,逻辑更简单,因为 Gradle 只能查找与 gradle-daemon-jvm.properties 文件中的平台匹配的下载 URL。然后,如果本地找不到 JDK,则使用该 URL 下载 JDK。

用于禁用自动检测自动供应的属性会影响守护进程工具链解析逻辑

org.gradle.java.installations.auto-detect=false
org.gradle.java.installations.auto-download=false

配置供应 URL

目前没有用于配置此项的 CLI 选项。

默认情况下,updateDaemonJvm 任务尝试为各种平台(操作系统和架构)上与指定标准匹配的 JDK 生成下载 URL。Gradle 需要考虑的不仅仅是当前运行的平台,因为构建可能在不同的平台上运行。

Gradle 按照惯例,基于 X86_64AARCH64 架构为以下操作系统设置构建平台

可以通过 UpdateDaemonJvm 任务的 toolchainPlatforms 属性配置这些平台。

build.gradle.kts
tasks.named<UpdateDaemonJvm>("updateDaemonJvm") {
    val myPlatforms = mutableListOf(
        BuildPlatformFactory.of(
            org.gradle.platform.Architecture.AARCH64,
            org.gradle.platform.OperatingSystem.MAC_OS
        )
    )
    toolchainPlatforms.set(myPlatforms)
}
build.gradle
tasks.named("updateDaemonJvm") {
    def myPlatforms = [
        BuildPlatformFactory.of(
            org.gradle.platform.Architecture.AARCH64,
            org.gradle.platform.OperatingSystem.MAC_OS
        )
    ]
    toolchainPlatforms.set(myPlatforms)
}

Gradle 通过使用 配置的工具链下载仓库 来解析这些平台的 JDK 下载 URL。如果未配置此类仓库,并且 toolchainPlatforms 属性至少有一个值,则 updateDaemonJvm 任务将失败。

或者,用户可以使用 toolchainDownloadUrls 属性直接配置特定平台的 JDK URL。此属性是一个 Map<BuildPlatform, URI>,可以按以下示例所示进行配置

build.gradle.kts
tasks.named<UpdateDaemonJvm>("updateDaemonJvm") {
    toolchainDownloadUrls = mapOf(
        BuildPlatformFactory.of(org.gradle.platform.Architecture.AARCH64, org.gradle.platform.OperatingSystem.MAC_OS) to uri("https://server?platform=MAC_OS.AARCH64"),
        BuildPlatformFactory.of(org.gradle.platform.Architecture.AARCH64, org.gradle.platform.OperatingSystem.WINDOWS) to uri("https://server?platform=WINDOWS.AARCH64")
    )
}
build.gradle
tasks.named("updateDaemonJvm") {
    toolchainDownloadUrls = [(BuildPlatformFactory.of(org.gradle.platform.Architecture.AARCH64, org.gradle.platform.OperatingSystem.MAC_OS)) : uri("https://server?platform=MAC_OS.AARCH64"),
                             (BuildPlatformFactory.of(org.gradle.platform.Architecture.AARCH64, org.gradle.platform.OperatingSystem.WINDOWS)) : uri("https://server?platform=WINDOWS.AARCH64")]
}
由于与不同包中首先按字母顺序解析的其他类型存在命名冲突,因此 org.gradle.platform.Architectureorg.gradle.platform.OperatingSystem 需要完整的包名称。

运行 ./gradlew updateDaemonJvm 会产生以下结果

gradle/gradle-daemon-jvm.properties
#This file is generated by updateDaemonJvm
toolchainUrl.MAC_OS.AARCH64=https\://server?platform\=MAC_OS.AARCH64
toolchainUrl.WINDOWS.AARCH64=https\://server?platform\=WINDOWS.AARCH64
toolchainVersion=17

如果您想禁用 updateDaemonJvm 任务生成 URL

build.gradle.kts
tasks.named<UpdateDaemonJvm>("updateDaemonJvm") {
    toolchainDownloadUrls.empty()
}
build.gradle
tasks.named("updateDaemonJvm") {
    toolchainPlatforms = []
}

删除所有平台意味着不再需要配置 工具链下载仓库

工具和 IDE

IDE 和其他工具用于与 Gradle 集成的 Gradle Tooling API 始终 使用 Gradle 守护进程来执行构建。如果您从 IDE 中执行 Gradle 构建,则您已经在使用 Gradle 守护进程。无需为您的环境启用它。

持续集成

我们建议为开发人员机器和持续集成 (CI) 服务器使用守护进程。

兼容性

如果没有空闲或兼容的守护进程存在,Gradle 会启动一个新的守护进程。

以下值确定兼容性

  • 请求的构建环境,包括以下内容

    • Java 版本

    • JVM 属性

    • JVM 属性

  • Gradle 版本

兼容性基于这些值的完全匹配。例如

  • 如果守护进程在 Java 8 运行时可用,但请求的构建环境需要 Java 10,则守护进程不兼容。

  • 如果守护进程在运行 Gradle 7.0 的情况下可用,但当前构建使用 Gradle 7.4,则守护进程不兼容。

Java 运行时的某些属性是不可变的:一旦 JVM 启动,就无法更改它们。以下 JVM 系统属性是不可变的

  • file.encoding

  • user.language

  • user.country

  • user.variant

  • java.io.tmpdir

  • javax.net.ssl.keyStore

  • javax.net.ssl.keyStorePassword

  • javax.net.ssl.keyStoreType

  • javax.net.ssl.trustStore

  • javax.net.ssl.trustStorePassword

  • javax.net.ssl.trustStoreType

  • com.sun.management.jmxremote

由启动参数控制的以下 JVM 属性也是不可变的

  • 最大堆大小(-Xmx JVM 参数)

  • 最小堆大小(-Xms JVM 参数)

  • 引导类路径(-Xbootclasspath 参数)

  • “断言”状态(-ea 参数)

如果请求的构建环境对这些属性和属性的任何要求与守护进程的 JVM 要求不同,则守护进程不兼容。

有关构建环境的更多信息,请参阅 构建环境文档

性能影响

当您重复构建同一项目时,守护进程可以将构建时间减少 15-75%。

在构建之间,守护进程空闲等待下一次构建。因此,您的机器仅为多次构建将 Gradle 加载到内存中一次,而不是每次构建一次。这是一个重要的性能优化。

运行时代码优化

JVM 从运行时代码优化中获得显着的性能提升:应用于代码在运行时进行的优化。

像 OpenJDK 的 Hotspot 这样的 JVM 实现会在执行期间逐步优化代码。因此,后续构建可以更快,这纯粹是由于这种优化过程。

使用守护进程,在项目的第 1 次和第 10 次构建之间,感知到的构建时间可能会大幅下降。

内存缓存

守护进程支持跨构建的内存缓存。这包括插件和构建脚本的类。

同样,守护进程维护构建数据的内存缓存,例如任务输入和输出的哈希值,用于增量构建。

性能监控

Gradle 主动监控堆使用情况,以检测守护进程中的内存泄漏。

当内存泄漏耗尽可用堆空间时,守护进程

  1. 完成当前正在运行的构建。

  2. 在运行下一次构建之前重新启动。

Gradle 默认启用此监控。

要禁用此监控,请将 org.gradle.daemon.performance.enable-monitoring 守护进程选项设置为 false

您可以使用以下命令在命令行上执行此操作

$ gradle <task> -Dorg.gradle.daemon.performance.enable-monitoring=false

或者,您可以在项目根目录或您的 GRADLE_USER_HOME (Gradle 用户主目录) 中的 gradle.properties 文件中配置该属性

gradle.properties
org.gradle.daemon.performance.enable-monitoring=false