Gradle Daemon(守护进程)
守护进程(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
文件中
org.gradle.daemon=true
禁用 Daemon
您可以通过多种方式禁用 Daemon,但有一些重要的注意事项
- 单次使用 Daemon
-
如果客户端进程的 JVM 参数与构建所需的不匹配,则会创建一个单次使用的 Daemon(一次性 JVM)。这意味着构建需要 Daemon,因此它会被创建、使用,然后在构建结束时停止。
- 不使用 Daemon
-
如果
JAVA_OPTS
和GRADLE_OPTS
与org.gradle.jvmargs
匹配,则根本不会使用 Daemon,因为构建发生在客户端 JVM 中。
为单个构建禁用
要为单个构建禁用 Daemon,请在运行构建时传递 --no-daemon
标志
$ gradle <task> --no-daemon
此标志会覆盖项目中启用 Daemon 的任何设置,包括 gradle.properties
文件。
为用户禁用
在 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 版本在以下各项中匹配:
-
您的环境 (
JAVA_HOME
) -
您的构建脚本(如果使用工具链)
-
您的 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_HOME
和 org.gradle.java.home
。
Daemon JVM 标准
Daemon JVM 标准由 updateDaemonJvm
任务控制,类似于wrapper 任务更新 wrapper 属性文件的方式。
当任务运行时,它会在 gradle/gradle-daemon-jvm.properties
文件中创建或更新标准。
要配置生成,可以使用命令行选项
$ ./gradlew updateDaemonJvm --jvm-version=17
或在根项目的构建脚本中配置任务
tasks.named<UpdateDaemonJvm>("updateDaemonJvm") {
languageVersion = JavaLanguageVersion.of(17)
}
tasks.named("updateDaemonJvm") {
languageVersion = JavaLanguageVersion.of(17)
}
然后运行任务
$ ./gradlew updateDaemonJvm
这两个操作都会生成如下文件
#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 |
AdoptOpenJDK |
|
ADOPTOPENJDK |
Amazon Corretto |
|
AMAZON |
Apple |
|
APPLE |
Azul Zulu |
|
AZUL |
BellSoft |
|
BELLSOFT |
GraalVM |
|
GRAAL_VM |
Hewlett Packard |
|
HEWLETT_PACKARD |
IBM |
|
IBM |
JetBrains |
|
JETBRAINS |
Microsoft |
|
MICROSOFT |
Oracle |
|
ORACLE |
SAP |
|
SAP |
Tencent |
|
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_64
和 AARCH64
设置构建平台
这些平台可以通过 UpdateDaemonJvm
任务的 toolchainPlatforms
属性进行配置。
tasks.named<UpdateDaemonJvm>("updateDaemonJvm") {
val myPlatforms = mutableListOf(
BuildPlatformFactory.of(
org.gradle.platform.Architecture.AARCH64,
org.gradle.platform.OperatingSystem.MAC_OS
)
)
toolchainPlatforms.set(myPlatforms)
}
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>
,可以按以下示例所示进行配置
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")
)
}
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.Architecture 和 org.gradle.platform.OperatingSystem 需要完整的包名。 |
运行 ./gradlew updateDaemonJvm
会生成以下内容
#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
tasks.named<UpdateDaemonJvm>("updateDaemonJvm") {
toolchainDownloadUrls.empty()
}
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 次构建的感知构建时间可以显著缩短。
性能监控
Gradle 会主动监控堆使用情况以检测 Daemon 中的内存泄漏。
当内存泄漏耗尽可用堆空间时,Daemon 会:
-
完成当前正在运行的构建。
-
在运行下一个构建之前重新启动。
Gradle 默认启用此监控。
要禁用此监控,请将 org.gradle.daemon.performance.enable-monitoring
Daemon 选项设置为 false
。
您可以在命令行上使用以下命令执行此操作
$ gradle <task> -Dorg.gradle.daemon.performance.enable-monitoring=false
或者您可以在项目根目录或您的 GRADLE_USER_HOME (Gradle 用户主目录) 的 gradle.properties
文件中配置该属性
org.gradle.daemon.performance.enable-monitoring=false