Gradle встраивает в Docker контейнер, занимающий слишком много памяти - PullRequest
4 голосов
/ 21 апреля 2020

Мне нужна ваша помощь, чтобы ограничить объем памяти, который собирает Gradle на сборочной машине. Я пытаюсь создать проект Android, который имеет почти 100 модулей, включая один модуль com.android.application и много com.android.library. Одна команда Gradle выполняет сборку, сборку и тесты (включая тесты Robolectri c, если это имеет значение).

Я запускаю сборку в Docker контейнере, основанном на jetbrains / TeamCity агент . Контейнер ограничен использованием только 10 ГБ из 64 ГБ оперативной памяти хоста с опцией mem_limit.

Сборки недавно начали давать сбой, поскольку они занимали слишком много памяти, и один из процессов был убит хост, который я мог видеть, запустив 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

Я наблюдал за использованием памяти в определенном сценарии ios с использованием top, и я заметил, что один процесс java (демон Gradle) медленно занимал более 8 ГБ, в то время как gradle.properties имел org.gradle.jvmargs=-Xmx4g, так что это слишком много, чем я ожидал.

Я попробовал несколько способов настроить Gradle, чтобы каким-то образом ограничить использование памяти, но мне это не удалось. Я попробовал следующие настройки в различных комбинациях:

  1. Я обнаружил, что JVM не знает об ограничениях памяти Docker и ей нужно -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap, поэтому я добавил его и удалил Xmx4g. Не помогло Использование Xmx4g было почти таким же.
  2. Я нашел -XX:MaxRAMFraction, но я думаю, что он не сильно изменился.
  3. Я попытался Xmx2g, но сбой при сборке вывел OutOfMemoryError: GC overhead limit exceeded в какой-то момент.
  4. Я попытался -XX:MaxPermSize=1g -XX:MaxMetaspaceSize=1g, который повесил сборку и выбрасывал OutOfMemoryError: Metaspace, поэтому я увеличил их до 2g, что, я думаю, не ограничивало общее использование памяти.
  5. Я пытался ограничить число рабочих до 4 и 1.
  6. Я обнаружил, что компилятор Kotlin может использовать другую стратегию выполнения: -Dkotlin.compiler.execution.strategy="in-process". Я думаю, это могло бы немного помочь, потому что было меньше процессов, потребляющих память, но процесс-демон все еще занимал более 8 ГБ.
  7. Использование GRADLE_OPTS с -Dorg.gradle.jvmargs="-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=10" -Dkotlin.compiler.execution.strategy="in-process", казалось, немного ограничивало память (демон потребовалось около 4,5 ГБ, так что это выглядело многообещающе) но просто повесил сборку с Expiring Daemon because JVM heap space is exhausted Я попробую другую фракцию тогда. Я полагаю, что 4 - это значение по умолчанию, верно?

Я также запутался, как параметры передаются в демон Gradle и Gradle CLI, поэтому я попробовал несколько различных комбинаций (но не все возможные комбинации, чтобы я мог легко что-то пропустить) из gradle.properties, JAVA_OPTS и GRADLE_OPTS с org.gradle.jvmargs внутри. Было бы полезно как-то распечатать фактические аргументы JVM, используемые демоном. Единственный способ, которым я пытался это выяснить, - это использовать ps -x и проверять аргументы, передаваемые процессам java.

Я должен упомянуть, что я пробовал все эти вещи, используя опцию --no-daemon и Gradle 6.2 0,2. Сейчас я пробую Gradle 6.3, но не ожидаю, что это поможет.

Если я запускаю сборку локально, используя команду Android Studio или Gradle на MacBook с 16 ГБ ОЗУ, это никогда не дает сбой из-за проблемы с ограничением памяти. Эта проблема возникает только на сервере сборки.

Я понятия не имею, что делать дальше, чтобы ограничить объем памяти, используемый процессом сборки. Мои единственные оставшиеся идеи, которые я еще не испробовал:

  1. Установка ограничений в задачах Gradle, например, tasks.withType(JavaCompile) { ... }. Поможет ли это?
  2. Удаление менее значимых плагинов Gradle, которые я использую, например, com.autonomousapps.dependency-analysis

К сожалению, все тесты очень трудоемки, потому что одна сборка может занять столько же времени, сколько и 30-60 минут, чтобы выполнить в зависимости от условий, поэтому я хотел бы избежать слепого тестирования.

Я делаю все это неправильно? Есть ли другие варианты ограничения памяти? Есть ли способ проанализировать, что занимает столько памяти? Можно ли форсировать G C в процессе сборки? Должен ли я задать другой вопрос вместо этого?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...