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

Gradle 运行在 Java 虚拟机 (JVM) 上,并使用几个支持库,它们的初始化时间不短。启动可能很慢。**Gradle Daemon(守护进程)**解决了这个问题。

Gradle Daemon 是一个长生命周期的后台进程,它可以缩短运行构建所需的时间。

Gradle Daemon 通过以下方式缩短构建时间:

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

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

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

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

理解守护进程

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

守护进程使用 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 文件中添加以下设置

gradle.properties
org.gradle.daemon=true

禁用守护进程

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

一次性守护进程

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

不使用守护进程

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

为单个构建禁用

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

$ gradle <task> --no-daemon

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

为项目禁用

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

为用户禁用

在 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

全局禁用

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

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

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

如果您想完全禁用守护进程,而不仅仅是调用一次性守护进程,请别忘了确保您的 JVM 参数与 GRADLE_OPTS / JAVA_OPTS 匹配。

停止守护进程

在故障排除或调试失败时,停止守护进程可能会有帮助。

满足以下任何条件时,守护进程会自动停止

  • 可用系统内存不足

  • 守护进程已闲置 3 小时

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

$ gradle --stop

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

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

守护进程故障排除

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

如果构建开始出现意外行为,请尝试停止并重新启动守护进程

$ gradle --stop

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

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

为了解决这个问题,请确保您的 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 设置以确保它们也匹配。

配置守护进程 JVM

守护进程 JVM 发现和标准是孵化中功能,未来版本可能会有所变更。

默认情况下,Gradle Daemon 使用启动构建的同一个 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 属性文件的方式。

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

任务运行时,它会在 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” 将需要供应商名称的精确匹配。

请求支持 native-image 的 JDK

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

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

更多信息请参见工具链文档章节

自动检测和自动供应

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

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

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

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) 服务器上使用守护进程。

兼容性

如果不存在空闲或兼容的守护进程,Gradle 将启动新的守护进程。

以下值决定兼容性

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

    • Java 版本

    • JVM 属性 (attributes)

    • JVM 属性 (properties)

  • 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