升级K2 2.1.20与Dagger 2.55后CI中OOM/Java堆内存问题频发的排查求助及疑问解答
升级K2 2.1.20与Dagger 2.55后CI中OOM/Java堆内存问题频发的排查求助及疑问解答
问题背景
我们团队在升级到Kotlin 2.1.20和Dagger 2.55后,CI环境下频繁遭遇java.lang.OutOfMemoryError: Java heap space错误,主要出现在两个核心场景:
- 运行包含800+模块的单元测试分片时
- 构建带有多个动态功能模块(DFM)的Release App Bundle时
报错集中在资源相关的Gradle任务,比如:
:calling.ui.lightweight:mergeDebugUnitTestResources:calling.ui.banners:mergeDebugUnitTestResources:Teams:mergeDevReleaseResources:Teams:mergeDevDebugResources:Teams:shrinkJioReleaseRes:remoteassist:mergeExtDexDevRelease
已尝试的排查与调整手段
针对这个问题,我们已经做了多轮尝试:
- 调整Kotlin Daemon内存配置:在
org.gradle.jvmargs中配置kotlin.daemon.jvm.options,尝试过将Kotlin Daemon的最大堆内存从1GB调到14GB不等 - Room注解处理器切换:暂时换回Kapt替代KSP来处理Room
- 垃圾回收器调整:将Kotlin Daemon的GC切换为升级前正常工作的G1GC
- Gradle JVM参数调整:测试过不同的堆内存配置,比如升级前可用的参数组合:
-Dorg.gradle.jvmargs="-Xss10m -XX:MaxHeapSize=13g -XX:MaxMetaspaceSize=2g -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/reports/heapDumps" -Dorg.gradle.daemon=false -Dorg.gradle.java.home=/usr/lib/jvm/temurin-17-jdk-amd64 -Dhttp.socketTimeout=300000 -Dhttp.connectionTimeout=300000
环境与当前Gradle配置
我们的CI代理机器配备32GB内存,同时混用KSP和KAPT:
- KSP仅用于Room的注解处理
- KAPT用于DataBinding、Dagger等其他注解处理器
核心Gradle Properties配置
org.gradle.daemon=true org.gradle.parallel=true org.gradle.configureondemand=true org.gradle.caching=true org.gradle.vfs.watch=true systemProp.org.gradle.internal.http.connectionTimeout=300000 systemProp.org.gradle.internal.http.socketTimeout=300000 systemProp.org.gradle.internal.repository.max.tentatives=10 systemProp.org.gradle.internal.repository.initial.backoff=500 org.gradle.jvmargs=-XX:+UseParallelGC -Xss4m -Xms2g -Xmx12g -XX:MaxMetaspaceSize=2g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:HeapDumpPath=/reports/heapDumps org.gradle.configuration-cache=true org.gradle.configuration-cache.problems=warn org.gradle.configuration-cache.max-problems=10 android.useAndroidX=true android.enableJetifier=true android.lifecycleProcessor.incremental=true android.enableResourceOptimizations=true android.nonFinalResIds=false android.defaults.buildfeatures.resvalues=false android.defaults.buildfeatures.shaders=false android.injected.androidTest.leaveApksInstalledAfterRun=true android.enableNewResourceShrinker.preciseShrinking=false android.experimental.lint.analysisPerComponent=false kapt.incremental.apt=true kapt.use.jvm.ir=false kapt.use.k2=false ksp.useKSP2=false kotlin.incremental.usePreciseJavaTracking=true roborazzi.test.record=true roborazzi.record.filePathStrategy=relativePathFromCurrentDirectory
最新进展与待解答疑问
经过调整Gradle最大工作线程数(我们CI机器最少8核,先设置为4),连续15次单元测试都没再出现堆内存问题,目前看起来效果不错,接下来会验证Release APK/App Bundle的构建情况。
我完全认同“减少复杂度比加内存更有效”这个观点,但还有几个点不太明白,想请教大家:
- 你提到的“maybe run in dependency to avoid excessive parallelism”具体是什么意思?能不能展开解释下?
- 如果模块中没有Java代码,Java编译任务会不会占用足够多的内存导致OOM?
- 你说的“Even when they are required, this might help to narrow down the problem.”这句话我没理解,麻烦再解释下?
另外补充几个我们的配置细节,供大家参考:
- 单元测试启用了Robolectric并包含Android资源,所以会触发资源相关任务:
testOptions { unitTests { includeAndroidResources = true } } - 我们只在必要场景使用KAPT(DataBinding和Dagger),应该没有多余的KAPT配置,但会再仔细检查
- Dagger即将支持KSP2,但DataBinding目前没有明确的支持计划,所以暂时没法完全切换到KSP
- 目前仅在一个模块用KSP处理Room,考虑过移除KSP来简化流程
- 移除Jetifier的工作正在推进,还有3-4个依赖库仍在使用Support库
- 无法关闭
android.enableResourceOptimizations,因为我们需要监控每个PR的APK大小变化,关闭后APK体积会大幅增加 - Roborazzi仅在4个模块中使用,应该不是导致堆内存问题的原因
内容来源于stack exchange




