【发布时间】:2020-08-04 02:18:11
【问题描述】:
我需要您的帮助来限制 Gradle 构建在构建机器上占用的内存量。我尝试构建一个包含近 100 个模块的 Android 项目,包括一个 com.android.application 模块和很多 com.android.library。单个 Gradle 命令同时执行构建、lint 和测试(如果重要,还包括 Robolectric 测试)。
我在基于 jetbrains/teamcity-agent 的 Docker 容器中运行构建。使用 mem_limit 选项,容器只能使用 64GB 主机 RAM 中的 10GB。
构建最近开始失败,因为它们占用了太多内存,并且其中一个进程被主机杀死,我可以通过在主机上运行 dmesg 看到这一点。它可能看起来像这样:
[3377661.066812] Task in /docker/3e5e65a5f99a27f99c234e2c3a4056d39ef33f1ee52c55c4fde42ce2f203942f killed as a result of limit of /docker/3e5e65a5f99a27f99c234e2c3a4056d39ef33f1ee52c55c4fde42ce2f203942f
[3377661.066816] memory: usage 10485760kB, limit 10485760kB, failcnt 334601998
[3377661.066817] memory+swap: usage 0kB, limit 9007199254740988kB, failcnt 0
[3377661.066818] kmem: usage 80668kB, limit 9007199254740988kB, failcnt 0
[3377661.066818] Memory cgroup stats for /docker/3e5e65a5f99a27f99c234e2c3a4056d39ef33f1ee52c55c4fde42ce2f203942f: cache:804KB rss:10404288KB rss_huge:0KB shmem:0KB mapped_file:52KB dirty:12KB writeback:0KB inactive_anon:1044356KB active_anon:9359932KB inactive_file:348KB active_file:72KB unevictable:0KB
[3377661.066826] [ pid ] uid tgid total_vm rss pgtables_bytes swapents oom_score_adj name
[3377661.066919] [ 6406] 0 6406 5012 14 81920 74 0 run-agent.sh
[3377661.066930] [ 8562] 0 8562 569140 2449 319488 6762 0 java
[3377661.066931] [ 8564] 0 8564 1553 20 53248 7 0 tail
[3377661.066936] [ 9342] 0 9342 2100361 92901 2437120 36027 0 java
[3377661.067178] [31134] 0 31134 1157 17 57344 0 0 sh
[3377661.067179] [31135] 0 31135 5012 83 77824 0 0 bash
[3377661.067181] [31145] 0 31145 1233001 21887 499712 0 0 java
[3377661.067182] [31343] 0 31343 4356656 2412172 23494656 0 0 java
[3377661.067195] [13020] 0 13020 56689 39918 413696 0 0 aapt2
[3377661.067202] [32227] 0 32227 1709308 30383 565248 0 0 java
[3377661.067226] Memory cgroup out of memory: Kill process 31343 (java) score 842 or sacrifice child
[3377661.067240] Killed process 13020 (aapt2) total-vm:226756kB, anon-rss:159668kB, file-rss:4kB, shmem-rss:0kB
我正在使用 top 观察某些场景中的内存使用情况,我注意到单个 java 进程(Gradle 守护进程)正在慢慢占用超过 8GB,而 gradle.properties 有 org.gradle.jvmargs=-Xmx4g 所以太多了超出我的预期。
我尝试了一些方法来配置 Gradle 以某种方式限制内存使用,但我失败了。我试过以下设置是各种组合:
- 我发现 JVM 不知道 Docker 内存限制,它需要
-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap,所以我添加并删除了Xmx4g。没有帮助。使用Xmx4g几乎是一样的。 - 我找到了
-XX:MaxRAMFraction,但我认为它没有太大变化。 - 我尝试了
Xmx2g,但它在某个时候抛出OutOfMemoryError: GC overhead limit exceeded导致构建失败。 - 我尝试了
-XX:MaxPermSize=1g -XX:MaxMetaspaceSize=1g,它挂起构建并抛出OutOfMemoryError: Metaspace,所以我将它们增加到2g,这不会限制整体内存使用量,我认为。 - 我尝试将工作人员的数量限制为 4 和 1。
- 我发现 Kotlin 编译器可以使用不同的执行策略:
-Dkotlin.compiler.execution.strategy="in-process"。我想这可能会有所帮助,因为消耗内存的进程较少,但守护进程仍然占用超过 8GB。 - 使用
GRADLE_OPTS设置为-Dorg.gradle.jvmargs="-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=10" -Dkotlin.compiler.execution.strategy="in-process"似乎会稍微限制内存(守护进程占用了大约 4.5GB,因此看起来很有希望)但它只是用Expiring Daemon because JVM heap space is exhausted挂起构建。我会尝试另一个分数。我想 4 是默认值,对吧?
我也很困惑参数是如何传递给 Gradle 守护程序 vs Gradle CLI,所以我尝试了gradle.properties、JAVA_OPTS 和 @ 的一些不同组合(但不是所有可能的组合,所以我很容易错过一些东西) 987654346@ 里面有org.gradle.jvmargs。能够以某种方式打印守护程序使用的实际 JVM 参数会很有帮助。我试图弄清楚的唯一方法是使用ps -x 并检查传递给java 进程的参数。
我应该提到我使用 --no-daemon 选项和 Gradle 6.2.2 尝试了所有这些东西。我现在正在试用 Gradle 6.3,但我不希望它有帮助。
如果我在 16GB RAM 的 MacBook 上使用 Android Studio 或 Gradle 命令在本地运行构建,它永远不会因为内存限制问题而失败。此问题仅发生在构建服务器上。
我不知道接下来要做什么来限制构建过程使用的内存量。我还没有尝试过的唯一剩下的想法是:
- 在 Gradle 任务中设置限制,例如喜欢
tasks.withType(JavaCompile) { ... }。这会有帮助吗? - 删除我使用的不太相关的 Gradle 插件,例如
com.autonomousapps.dependency-analysis
不幸的是,所有测试都非常耗时,因为单个构建可能需要长达 30-60 分钟才能执行,具体取决于条件,因此我希望避免盲目测试。
我做错了吗?还有其他限制内存的选项吗?有什么方法可以分析什么占用了这么多内存?是否可以在构建过程中强制 GC?我应该问一个不同的问题吗?
【问题讨论】:
-
嗨@Andrzej,你找到解决办法了吗?
-
不,我没有,不幸的是????@AbdelghaniRoussi
-
我也有同样的问题,如果我找到解决方案会更新你的 Q :D
-
@AndrzejZabost 你可以使用这个
-XX:+PrintFlagsFinal来检查应用到 jvm 的标志,希望这会有所帮助
标签: java android docker kotlin gradle