Gradle Daemon(守护进程)
守护进程 (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
文件中添加以下设置
org.gradle.daemon=true
禁用守护进程
您可以通过多种方式禁用守护进程,但有一些重要注意事项
- 一次性守护进程
-
如果客户端进程的 JVM 参数与构建所需的不匹配,则会创建一个一次性守护进程(可丢弃的 JVM)。这意味着构建需要守护进程,因此它被创建、使用,然后在构建结束时停止。
- 不使用守护进程
-
如果
JAVA_OPTS
和GRADLE_OPTS
与org.gradle.jvmargs
匹配,则根本不会使用守护进程,因为构建发生在客户端 JVM 中。
为单个构建禁用
要为单个构建禁用守护进程,请在运行构建时传递 --no-daemon
标志
$ gradle <task> --no-daemon
此标志会覆盖项目中(包括 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
全局禁用
有两种推荐的方式可以在环境中全局禁用守护进程
-
在
$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 版本在以下方面匹配:
-
您的环境 (
JAVA_HOME
) -
您的构建脚本(如果使用工具链)
-
您的 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_HOME
和 org.gradle.java.home
。
守护进程 JVM 标准
“守护进程 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
任务时不带任何参数,并且属性文件不存在,则将使用守护进程当前使用的 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 |
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
更多信息请参见工具链文档章节。
自动检测和自动供应
守护进程 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_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) 服务器上使用守护进程。
兼容性
如果不存在空闲或兼容的守护进程,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 主动监控堆使用量以检测守护进程中的内存泄漏。
当内存泄漏耗尽可用堆空间时,守护进程会
-
完成当前正在运行的构建。
-
在运行下一次构建之前重新启动。
Gradle 默认启用此监控。
要禁用此监控,请将 org.gradle.daemon.performance.enable-monitoring
守护进程选项设置为 false
。
您可以在命令行上使用以下命令执行此操作
$ gradle <task> -Dorg.gradle.daemon.performance.enable-monitoring=false
或者您可以在项目根目录或您的 GRADLE_USER_HOME (Gradle 用户主目录) 下的 gradle.properties
文件中配置该属性
org.gradle.daemon.performance.enable-monitoring=false