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

Gradle 运行在 Java 虚拟机 (JVM) 上,并使用多个支持库,这些库的初始化时间不可忽略。启动可能很慢。Gradle Daemon 解决了这个问题。

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

Gradle Daemon 通过以下方式减少构建时间:

  • 在构建之间缓存项目信息

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

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

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

理解 Daemon

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

Daemon 使用 JVM 的默认最小堆大小。

如果请求的构建环境未指定最大堆大小,Daemon 最多使用 512MB 的堆。512MB 对于大多数构建来说已经足够。包含数百个子项目、配置和源代码的较大构建可能会受益于更大的堆大小。

检查 Daemon 状态

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

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

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

查找 Daemon

如果您已安装 Java 开发工具包 (JDK),可以使用 jps 命令查看活动的 Daemon。

$ jps
33920 Jps
27171 GradleDaemon
22792

活动的 Daemon 会以 GradleDaemon 的名称显示。由于此命令使用 JDK,您可以查看运行任何 Gradle 版本的 Daemon。

启用 Daemon

自 Gradle 3.0 起,Gradle 默认启用 Daemon。如果您的项目没有使用 Daemon,您可以在运行构建时使用 --daemon 标志为单个构建启用它

$ gradle <task> --daemon

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

要在旧版 Gradle 中默认启用 Daemon,请将以下设置添加到项目根目录或您的 Gradle 用户主目录 (GRADLE_USER_HOME) 中的 gradle.properties 文件中

gradle.properties
org.gradle.daemon=true

禁用 Daemon

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

单次使用 Daemon

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

不使用 Daemon

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

为单个构建禁用

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

$ gradle <task> --no-daemon

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

为项目禁用

要为项目的所有构建禁用 Daemon,请在项目根目录gradle.properties 文件中添加 org.gradle.daemon=false

为用户禁用

在 Windows 上,此命令会禁用当前用户的 Daemon

(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 命令会禁用当前用户的 Daemon

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

全局禁用

有两种推荐的方法可以全局禁用环境中的 Daemon

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

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

如果您想完全禁用 Daemon,而不仅仅是调用一次性 Daemon,请务必确保您的 JVM 参数和 GRADLE_OPTS / JAVA_OPTS 匹配。

停止 Daemon

在故障排除或调试失败时停止 Daemon 会很有帮助。

满足以下任何条件时,Daemon 会自动停止

  • 可用系统内存不足

  • Daemon 已空闲 3 小时

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

$ gradle --stop

这会终止所有使用与执行此命令相同 Gradle 版本启动的 Daemon 进程。

您也可以使用操作系统手动杀死 Daemon。要查找所有 Daemon 的 PID(无论 Gradle 版本如何),请参阅查找 Daemon

Daemon 故障排除

Gradle Daemon 是一个长期运行的后台进程,因此有时会遇到问题。

如果构建开始出现异常行为,请尝试停止并重新启动 Daemon

$ gradle --stop

如果您看到类似以下警告:Multiple Gradle daemons might be spawned because the Gradle JDK and JAVA_HOME locations are different

Gradle 告诉您它在您的环境中使用了不止一个 Java 版本。这可能导致启动多个 Daemon,不必要地增加内存使用。

要解决此问题,请确保您的 Java 版本在以下各项中匹配:

  1. 您的环境 (JAVA_HOME)

  2. 您的构建脚本(如果使用工具链)

  3. 您的 IDE 配置的 JDK

要检查 JAVA_HOME,请在您的终端中运行此命令

echo $JAVA_HOME

您的项目可能在 build.gradle(.kts) 文件中使用了特定的工具链。检查类似的代码

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(11)
    }
}

如果您的构建使用工具链,请确保它与 JAVA_HOME 值匹配,或者至少知道它们是故意不同的。您还应该检查您的 IDE 设置以确保它们也匹配。

配置 Daemon JVM

Daemon JVM 发现和标准是孵化中的功能,可能会在未来版本中发生变化。

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

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

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

如果提供了 Daemon JVM 标准配置,它将优先于 JAVA_HOMEorg.gradle.java.home

Daemon JVM 标准

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

此过程需要配置工具链下载仓库。有关详细信息,请参见下文

当任务运行时,它会在 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 任务,并且属性文件不存在,则将使用 Daemon 当前 JVM 的版本。

在下次执行构建时,Gradle 客户端将使用此文件来定位兼容的 JVM 安装并启动 Daemon。

与 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”将需要供应商名称的精确匹配。

请求支持 native-image 的 JDK

CLI 选项和任务配置都允许请求支持 native-image 的 JDK。

$ ./gradlew updateDaemonJvm --jvm-version=17 --native-image-capable

有关详细信息,请参阅工具链文档部分

自动检测和自动配置

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

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

用于禁用自动检测自动配置的属性会影响 Daemon 工具链解析逻辑

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 Daemon 来执行构建。如果您从 IDE 内部执行 Gradle 构建,您已经在使用 Gradle Daemon。无需为您的环境启用它。

持续集成

我们建议在开发人员机器和持续集成 (CI) 服务器上使用 Daemon。

兼容性

如果不存在空闲或兼容的 Daemon,Gradle 会启动一个新的 Daemon。

以下值决定兼容性

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

    • Java 版本

    • JVM 属性

    • JVM 参数

  • Gradle 版本

兼容性基于这些值的精确匹配。例如:

  • 如果有一个带有 Java 8 运行时环境的 Daemon 可用,但请求的构建环境要求 Java 10,则该 Daemon 不兼容。

  • 如果有一个运行 Gradle 7.0 的 Daemon 可用,但当前构建使用 Gradle 7.4,则该 Daemon 不兼容。

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 参数)

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

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

性能影响

当您重复构建同一项目时,Daemon 可以将构建时间缩短 15-75%。

在构建之间,Daemon 会空闲等待下一个构建。因此,您的机器只需为多个构建加载 Gradle 到内存中一次,而不是每个构建加载一次。这是一个显著的性能优化。

运行时代码优化

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

像 OpenJDK 的 Hotspot 这样的 JVM 实现会在执行过程中逐步优化代码。因此,后续构建可能会仅仅由于此优化过程而更快。

有了 Daemon,项目从第 1 次构建到第 10 次构建的感知构建时间可以显著缩短。

内存缓存

Daemon 实现了跨构建的内存缓存。这包括插件和构建脚本的类。

同样,Daemon 会维护构建数据的内存缓存,例如增量构建的任务输入和输出的哈希值。

性能监控

Gradle 会主动监控堆使用情况以检测 Daemon 中的内存泄漏。

当内存泄漏耗尽可用堆空间时,Daemon 会:

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

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

Gradle 默认启用此监控。

要禁用此监控,请将 org.gradle.daemon.performance.enable-monitoring Daemon 选项设置为 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