【我的Android进阶之旅】解决Jenkins编译出现的Gradle错误:Gradle build daemon disappeared unexpectedly

一、问题描述

同事在Jenkins新建一个job准备编译的时候,出现了如下所示的错误。

在这里插入图片描述

07:29:49.994 [DEBUG] [org.gradle.launcher.daemon.server.SynchronizedDispatchConnection] thread 15: received class org.gradle.launcher.daemon.protocol.CloseInput
07:29:49.994 [DEBUG] [org.gradle.launcher.daemon.server.DefaultDaemonConnection] thread 15: Received IO message from client: org.gradle.launcher.daemon.protocol.CloseInput@cef8b13
07:29:49.997 [DEBUG] [org.gradle.launcher.daemon.server.exec.LogToClient] About to start relaying all logs to the client via the connection.
07:29:49.998 [INFO] [org.gradle.launcher.daemon.server.exec.LogToClient] The client will now receive all logging from the daemon (pid: 2754). The daemon log file: /root/.gradle/daemon/5.0/daemon-2754.out.log
07:29:49.999 [INFO] [org.gradle.launcher.daemon.server.exec.LogAndCheckHealth] Starting build in new daemon [memory: 6.7 GB]
07:29:50.001 [INFO] [org.gradle.launcher.daemon.server.exec.ForwardClientInput] Closing daemon's stdin at end of input.
07:29:50.001 [INFO] [org.gradle.launcher.daemon.server.exec.ForwardClientInput] The daemon will no longer process any standard input.
07:29:50.006 [DEBUG] [org.gradle.launcher.daemon.server.exec.ExecuteBuild] The daemon has started executing the build.
07:29:50.007 [DEBUG] [org.gradle.launcher.daemon.server.exec.ExecuteBuild] Executing build with daemon context: DefaultDaemonContext[uid=4e7b7b5b-ab04-42a6-b751-4f5d41c062b9,javaHome=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.151-1.b12.el6_9.x86_64,daemonRegistryDir=/root/.gradle/daemon,pid=2754,idleTimeout=10800000,priority=NORMAL,daemonOpts=-XX:MaxPermSize=4096m,-XX:+HeapDumpOnOutOfMemoryError,-Xmx7g,-Dfile.encoding=UTF-8,-Duser.country=CN,-Duser.language=zh,-Duser.variant]
----- End of the daemon log -----


FAILURE: Build failed with an exception.

* What went wrong:
Gradle build daemon disappeared unexpectedly (it may have been killed or may have crashed)

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org
Build step 'Invoke Gradle script' changed build result to FAILURE
Build step 'Invoke Gradle script' marked build as failure

二、解决错误

2.1 加入参数 --no-daemon

参考链接:https://www.cnblogs.com/liushengchieh/p/9729786.html

在这里插入图片描述
按照上面的说法,我们加上了 --no-daemon参数

./gradlew clean lintForXTC && ./gradlew assembleRelease -PAPK_TYPE="xtc" 

改成了

./gradlew --no-daemon clean lintForXTC && ./gradlew --no-daemon assembleRelease -PAPK_TYPE="xtc"

在这里插入图片描述

加上了–no-daemon参数 还是一样的失败了。

2.2 重启节点编译服务器

重启大法好,有什么解决不了的事情的话,重启下电脑大部分问题可以解决。

Jenkins上配置的这台用于Android APP 编译的节点服务器,已经有很久很久的时间(大概半年多了)没有重启过了,因此可能会出现一些问题。

1、所以我将这台节点服务器和Jenkins服务器之间的联系断开。

在这里插入图片描述
进入到下面的页面,然后点击下面的【Mark this node temporarity offline】按钮,断开节点服务器和Jenkins服务器之间的联系
在这里插入图片描述

在这里插入图片描述

2、然后使用同事 XShell客户端,SSH连接上这台用于Android APP 编译的节点服务器,并输入 reboot 命令重启节点服务器

在这里插入图片描述

3、重新连接节点服务器和Jenkins服务器
进入Jenkins配置页面,现在的节点服务器处于offline这台。
在这里插入图片描述

点击【Bring this node back online】按钮

在这里插入图片描述

现在节点服务器的状态是 在线的。
在这里插入图片描述
重新启动Jenkins的job进行编译,这才能编译成功!
在这里插入图片描述

该问题中途还尝试修改了其他的配置,都是没有用,只有重启才生效了!
在这里插入图片描述

2.3 编译服务器内存不够

PS:过了几天,又有同事出现了该问题。

在这里插入图片描述

Build request sent: Build{id=81f5d14d-acbb-4d16-a2f8-ecc3e25157fa.1, currentDir=/data/jenkins/workspace/AP_Release_6.0.8}
Attempting to read last messages from the daemon log...
Daemon pid: 11475
  log file: /root/.gradle/daemon/4.6/daemon-11475.out.log
----- Last  20 lines from daemon log file - daemon-11475.out.log -----
:audiorecord:transformResourcesWithMergeJavaResForRelease
:audiorecord:transformClassesAndResourcesWithSyncLibJarsForRelease
:audiorecord:compileReleaseNdk NO-SOURCE
:audiorecord:mergeReleaseJniLibFolders
:audiorecord:transformNativeLibsWithMergeJniLibsForRelease
:audiorecord:transformNativeLibsWithSyncJniLibsForRelease
:audiorecord:bundleReleaseAar
:audiorecord:compileReleaseSources
:audiorecord:mergeReleaseResources
:audiorecord:verifyReleaseResources
:audiorecord:assembleRelease
:basiclib:copyApiToJava UP-TO-DATE
:basiclib:preBuild UP-TO-DATE
:basiclib:preReleaseBuild UP-TO-DATE
:componentlib:preBuild UP-TO-DATE
:componentlib:preReleaseBuild UP-TO-DATE
:componentlib:compileReleaseAidl NO-SOURCE
:basiclib:compileReleaseAidl NO-SOURCE
:componentlib:packageReleaseRenderscript NO-SOURCE
:basiclib:compileReleaseRenderscript
----- End of the daemon log -----


FAILURE: Build failed with an exception.

* What went wrong:
Gradle build daemon disappeared unexpectedly (it may have been killed or may have crashed)

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org
Build step 'Execute shell' marked build as failure

通过SSH连接到此台编译服务器,查看指定的日志

 cat /root/.gradle/daemon/4.6/daemon-11475.out.log

在这里插入图片描述

接着看日志,下面的日志 是不是会是内存不够了,需要开辟6.7GB的内存来打开一个新的daemon。

00:30:21.033 [DEBUG] [org.gradle.launcher.daemon.server.SynchronizedDispatchConnection] thread 14: received class org.gradle.launcher.daemon.protocol.CloseInput
00:30:21.034 [DEBUG] [org.gradle.launcher.daemon.server.DefaultDaemonConnection] thread 14: Received IO message from client: org.gradle.launcher.daemon.protocol.CloseInput@6a5ee19e
00:30:21.037 [DEBUG] [org.gradle.launcher.daemon.server.exec.LogToClient] About to start relaying all logs to the client via the connection.
00:30:21.037 [INFO] [org.gradle.launcher.daemon.server.exec.LogToClient] The client will now receive all logging from the daemon (pid: 11475). The daemon log file: /root/.gradle/daemon/4.6/daemon-11475.out.log
00:30:21.039 [INFO] [org.gradle.launcher.daemon.server.exec.LogAndCheckHealth] Starting build in new daemon [memory: 6.7 GB]
00:30:21.040 [INFO] [org.gradle.launcher.daemon.server.exec.ForwardClientInput] Closing daemon's stdin at end of input.
00:30:21.040 [INFO] [org.gradle.launcher.daemon.server.exec.ForwardClientInput] The daemon will no longer process any standard input.
00:30:21.043 [DEBUG] [org.gradle.launcher.daemon.server.exec.ExecuteBuild] The daemon has started executing the build.
00:30:21.043 [DEBUG] [org.gradle.launcher.daemon.server.exec.ExecuteBuild] Executing build with daemon context: DefaultDaemonContext[uid=fe32d13f-f5d4-49a3-bfea-7696448a8047,javaHome=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.151-1.b12.el6_9.x86_64,daemonRegistryDir=/root/.gradle/daemon,pid=11475,idleTimeout=10800000,daemonOpts=-XX:MaxPermSize=4096m,-XX:+HeapDumpOnOutOfMemoryError,-Xmx7g,-Dfile.encoding=UTF-8,-Duser.country=CN,-Duser.language=zh,-Duser.variant]
project.gradle.gradleVersion : 4.6Observed package id 'platforms;android-21' in inconsistent location '/data/opt/android/sdk/platforms/android-5.0.1' (Expected '/data/opt/android/sdk/platforms/android-21')

在这里插入图片描述

查看下内存情况

在这里插入图片描述

[root@localhost 4.6]#   free
             total       used       free     shared    buffers     cached
Mem:      16336324   16149820     186504          0     299308    4192776
-/+ buffers/cache:   11657736    4678588
Swap:     10484728     267892   10216836
[root@localhost 4.6]#   free -m
             total       used       free     shared    buffers     cached
Mem:         15953      15788        165          0        291       4124
-/+ buffers/cache:      11372       4581
Swap:        10238        266       9972

执行下 gradle --stop,然后看看内存情况

在这里插入图片描述

[root@localhost 4.6]# gradle --stop
Stopping Daemon(s)
1 Daemon stopped
[root@localhost 4.6]# free
             total       used       free     shared    buffers     cached
Mem:      16336324    7981244    8355080          0     370872    5089496
-/+ buffers/cache:    2520876   13815448
Swap:     10484728     293092   10191636

停止之后,内存有了8001Mb的剩余。这个时候再去重启下Jenkins的job,可以正常编译了。

三、Gradle Daemon知识点

以下内容摘自
原文:https://docs.gradle.org/2.14.1/userguide/gradle_daemon.html
译文:https://blog.csdn.net/CSDN472651883/article/details/79004310


守护进程是一种作为后台进程运行的计算机程序,而不是由交互式用户直接控制。
Gradle在Java虚拟机(JVM)上运行,并使用一些支持库,这些库需要一定的初始化时间。因此,有时候看起来有点慢。解决这个问题的方法是启用Gradle Daemon(Daemon:一个长期的后台进程,比其他情况下执行你的构建要快得多)。我们通过避免昂贵的引导过程以及利用缓存来实现这一点,将有关项目的数据保存在内存中。运行Gradle与守护进程构建没有区别,进行简单地配置后无论你是否要使用它 都将由Gradle自行的处理。

3.1 启用守护进程

Gradle Daemon在默认情况下是不启用的,但是我们建议开发者的设备始终启用它(但是对于持续集成服务器,将其禁用)。启用守护进程有几种方法,但最常见的方法是添加该行

org.gradle.daemon =true

到文件/.gradle/gradle.properties下进行配置, 所在位置取决于您的系统,通常如下:

  • C:\Users\ (Windows Vista&7+
  • /Users/ (Mac OS X)
  • /home/ (Linux)

注:<USER_HOME>为当前用户的主目录,比如:C:\Users\Administrator那么这个<USER_HOME>指的就是Administrator。

如果该文件不存在,只需使用文本编辑器创建。您可以在下面的FAQ中找到启用(和禁用)守护进程的其他方法的详细信息。该部分还包含关于守护进程如何工作的更多详细信息。

一旦以这种方式全局启用了守护进程,不管特定构建使用的Gradle版本如何,所有的构建速度都将提升。

3.2 停止现有的守护进程

如前所述,守护进程是一个后台进程。但是,您不需要担心该后台进程对性能的影响,每个守护进程在三小时不活跃的情况下会自行停止。如果您因为一些原因想要停止守护进程,请使用该命令为:gradle –stop

这将终止所有用于执行命令的相同版本的Gradle启动的守护进程。如果安装了Java Development Kit(JDK),则可以通过运行jps命令轻松验证守护程序是否已停止。您将看到任何正在运行的名为GradleDaemon的守护进程。

3.3 常问问题

3.3.1 启用Gradle Daemon的方法有哪些?

有两种建议的方式来持续为一个环境启用守护进程:

  • 通过环境变量 - 将标志添加-Dorg.gradle.daemon=true到GRADLE_OPTS环境变量
  • 通过属性文件 -添加org.gradle.daemon=true到/gradle.properties文件

请注意,<GRADLE_USER_HOME>默认为<USER_HOME>/.gradle,<USER_HOME>为当前用户的主目录,此位置可以通过-g和–gradle-user-home命令行开关以及GRADLE_USER_HOME环境变量和org.gradle.user.homeJVM系统属性进行配置。

两种方法都有相同的效果,根据个人喜好来使用。大多数Gradle用户选择第二种方法,并将条目添加到用户gradle.properties文件。

在Windows上,启用命令为:

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

也就相当于,在C:\Users.gradle文件下的gradle.properties(如果没有进行创建)中配置echo org.gradle.daemon=true

在Linux环境下

    touch ~/.gradle/gradle.properties && echo "org.gradle.daemon=true" >> ~/.gradle/gradle.properties  

一旦以这种方式为构建环境启用了守护进程,所有构建都将隐式使用Daemon。
在个人的构建使用命令行界面时可以调用–daemon和–no-daemon命令行开关来启用和禁用守护程序。通常情况下,为一个环境(比如一个用户账号)启用守护进程会更方便,这样所有的版本都可以使用守护进程而不需要记住提供–daemon交换机

3.3.2 我如何禁用Gradle守护进程?

Gradle守护进程默认是不启用的。但是,一旦启用,有时需要禁用某些项目或某些构建时可以使用–no-daemon命令行开关进行强制禁用

该命令行很少使用,但是在调试某些构建或者Gradle插件的问题时有时会很有用。考虑到构建环境,该命令行开关的优先级最高。

3.3.3 如何抑制“please consider using the Gradle Daemon”消息?

Gradle可能会在构建结束时发出警告,提示您使用Gradle守护程序。为了避免这个警告,你可以通过上面的方法来启用守护进程,或者明确禁用守护进程。你可以明确地通过使用禁用守护进程–no-daemon如上所述命令行开关,或着开启的方法之一,但org.gradle.daemon属性的值应为false,而不是true。

由于不建议使用守护进程连续集成构建,所以如果CI环境变量存在,Gradle将不会发出消息。

3.3.4 为什么我的机器上有多个守护进程?

Gradle将创建一个新的守护进程有几个原因,而不是使用已经运行的守护进程。基本规则是:如果没有可用的空闲或兼容的守护进程,Gradle将启动一个新的守护进程。Gradle会杀死已经闲置3个小时以上的守护进程,所以你不必担心手动清理它们。

一个空闲的守护进程是一个目前没有执行构建或做其他有用的工作。

兼容

一个兼容的守护进程是可以(或可以做到)满足所要求的构建环境的要求的守护进程。用于执行构建的Java运行时是构建环境的示例方面。另一个示例是构建运行时所需的一组JVM系统属性。

守护进程可能无法满足所请求的构建环境的某些方面。如果守护程序正在Java 7环境下运行,但所请求的环境需要Java 8,则守护进程不兼容,必须启动另一个。而且,一旦JVM启动,Java运行时的某些属性就不能被改变。例如不能改变-Xmx1024m正在运行的JVM 的内存分配,默认文本编码,缺省语言环境等。

“requested build environmen”通常是由构建客户端(例如Gradle命令行客户端,IDE等)环境以及通过命令行开关和设置明确构建的。有关如何指定和控制构建环境的详细信息,请参见第11章 “构建环境”

以下JVM系统属性实际上是不可变的。如果请求的构建环境需要这些属性中的任何一个,并且当 Daemon的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参数)

Gradle版本和编译环境的版本不同是另一个方面。守护进程耦合到特定的Gradle运行时,在使用不同Gradle版本的会话期间处理多个Gradle项目是拥有多个运行中的守护进程的常见原因。

3.3.5 Daemon 使用多少内存,我可以给它多一点?

如果请求的构建环境没有指定最大堆大小,守护进程将使用最多1GB的堆。它将使用您的JVM的默认最小堆大小。1GB对于大多数版本来说已经足够了。更大的构建与数百个子项目,大量的配置和源代码可能需要,或性能更好,更多的内存。

要增加守护程序可以使用的内存量,请将相应的标志指定为所请求的生成环境的一部分。有关详细信息,请参阅第11章“构建环境 ”。

3.3.6 我怎样才能停止Daemon ?

不活动3小时后,守护进程将自动终止。如果您希望在此之前停止守护进程,则可以通过操作系统终止进程或运行gradle –stop命令。该–stop开关使得Gradle请求运行该命令的相同Gradle版本的所有正在运行的Daemon进程自行终止。

3.3.7 Daemon 有什么问题?

在守护进程日益发展的过程中,相当大的工程努力已经使守护进程变得健壮,透明和不显眼。但是,守护进程偶尔会被损坏或耗尽。Gradle构建执行来自多个源的任意代码。虽然Gradle本身是专门为Daemon设计的并且经过严格测试,但是用户构建脚本和第三方插件可能会通过内存泄漏或全局状态损坏等缺陷来破坏守护进程的稳定性。

通过运行不能正确释放资源的构建,也可能会破坏守护进程的稳定性(通常会构建环境)。在使用Microsoft Windows时,这是一个特别棘手的问题,因为它对读取或写入后无法关闭文件的程序宽容。

Gradle会主动监视堆的使用情况,并尝试检测泄漏是否开始耗尽守护进程中可用的堆空间。当它检测到堆空间的问题时,Gradle守护进程将完成当前正在运行的构建,并在下一个构建中重新启动守护进程。此监视默认启用,但可以通过将org.gradle.daemon.performance.enable-monitoring系统属性设置为false 来禁用。

如果怀疑守护进程变得不稳定,可能会被简单地杀死。回想一下,–no-daemon可以为构建指定开关,以防止使用守护进程。这对于诊断守护进程是否真的是问题的罪魁祸首可能是有用的

3.4 何时不应该使用Gradle守护进程?

建议在所有开发人员环境中使用守护进程并且不要启用用于持续集成的守护进程并构建服务器环境。

守护进程使构建更快,当人坐在构建前面时,这一点尤为重要。对于CI构建,稳定性和可预测性是至关重要的。对于每个构建使用全新的运行时(即进程)更加可靠,因为运行时与以前的构建完全隔离

3.5 工具和IDE

IDE和其他工具用于与Gradle集成 的Gradle Tooling API(请参阅第13章,使用Tooling API嵌入Gradle)始终使用Gradle Daemon来执行构建。如果从内部执行Gradle构建,则您使用的是Gradle守护程序,无需为您的环境启用它。

但是,除非您为您的环境明确启用了Gradle守护程序,否则命令行中的构建不会使用Gradle守护程序。

3.6 Gradle Daemon如何更快地构建?

Gradle守护进程是一个长期的构建过程。在构建之间,它等待下一个构建。这具有明显的好处,只需要将Gradle一次加载到多次构建中,而不是每次构建一次。这本身就是一个重要的性能优化,但这并不是停止的地方。

现代JVM性能的一个重要部分是运行时代码优化。例如,HotSpot(Oracle提供的用作OpenJDK基础的JVM实现)在代码运行时对其进行优化。优化是渐进的而不是瞬时的。也就是说,代码在执行过程中被逐步优化,这意味着后续的构建可以更快地完成,纯粹是由于这个优化过程。HotSpot的实验表明,它需要5到10之间的构建优化来稳定。守护程序的第一次构建和第十次构建时间之间的差异可能相当大。

守护进程还更有效的允许跨内存缓存。例如,构建所需的类(例如,插件,构建脚本)可以在构建之间保存在内存中。同样,Gradle可以维护内存缓存的构建数据,如任务输入和输出的散列,用于增量构建。

3.6.1 潜在的未来增强

目前,守护进程通过有效地支持内存缓存以及JVM优化器使代码更快,使构建更快。在未来的Gradle版本中,守护进程将变得更加智能,并且抢先进行工作。例如,假定构建即将运行,并且将需要新更改或添加的依赖关系,它可以在编译构建脚本之后立即开始下载依赖关系。

Gradle Daemon还有很多其他的方法可以在未来的Gradle版本中实现更快的构建


作者:欧阳鹏 欢迎转载,与人分享是进步的源泉!
转载请保留原文地址:https://blog.csdn.net/qq446282412/article/details/90317099
☞ 本人QQ: 3024665621
☞ QQ交流群: 123133153
☞ github.com/ouyangpeng
[email protected]


发布了469 篇原创文章 · 获赞 1467 · 访问量 359万+

猜你喜欢

转载自blog.csdn.net/qq446282412/article/details/90317099