Analyzing and Fixing CI Build Performance Degradation after Upgrading to JDK 11 in the Feishu Android Project
This article investigates why upgrading the Feishu Android project's CI build environment from JDK 8 to JDK 11 caused a 50% increase in build time, analyzes JVM, Gradle, and Docker interactions, and presents concrete JVM flag and environment‑variable solutions that restored parallelism and reduced build duration back to pre‑upgrade levels.
Abstract – The upgrade of Feishu’s Android CI from JDK 8 to JDK 11 triggered a severe slowdown in both main‑repo packaging and sub‑module AAR publishing, with build time rising from ~17 minutes to ~26 minutes (≈50% increase). The article details the investigation, root‑cause analysis, and remediation steps.
Background – After moving the targetSdkVersion and compileSdkVersion to 31, the team observed build failures and, following StackOverflow advice, upgraded the JDK used for CI from 8 to 11 while keeping AGP 4.1.0, anticipating future AGP 7.0 requirements.
Problem – Post‑upgrade, sub‑module AAR publishing became dramatically slower and main‑repo packaging time increased by about 9 minutes. Metrics showed a shift from concurrent to single‑threaded execution.
Analysis
1. Build tasks became single‑threaded because the JDK 11 runtime inside Docker reported only one available processor, whereas JDK 8 reported the full 96 cores.
2. The change was linked to the JVM’s container‑aware CPU detection (UseContainerSupport) introduced in JDK 8u191 and enabled by default in JDK 11.
3. Even after forcing parallelism with --max‑workers , the underlying processor count remained 1, so key tasks (ByteXTransform, DexBuilder) stayed serial.
4. GC settings also regressed: JDK 11 used SerialGC (single‑threaded) while JDK 8 used G1GC with many parallel threads, further inflating build time.
5. Native implementations of OperatingSystemMXBean.getAvailableProcessors() differ between JDK 8 and JDK 11; the latter consults Docker cgroup limits, yielding 1 core.
Fix
Set JVM flag -XX:ActiveProcessorCount=96 to manually specify core count, or disable container support with -XX:-UseContainerSupport so the JVM reads the host’s CPU count.
Prefer the latter for CI builds; it restores correct values for OsAvailableProcessors , ByteXTransform, and DexBuilder.
Apply the flag globally via JAVA_TOOL_OPTIONS environment variable, ensuring both the Gradle wrapper and daemon processes inherit the setting.
For mixed‑branch environments, dynamically select JDK version and only enable -XX:-UseContainerSupport when running on JDK 11.
Effect – After merging the fix on 06‑30, build times dropped back to pre‑upgrade levels, ByteXTransform and DexBuilder durations normalized, GC switched back to G1GC, and the OsAvailableProcessors field reported the correct core count.
Conclusion – The incident highlights the importance of testing core tooling (Gradle, AGP, Kotlin, JDK) under containerized CI environments, establishing degradation detection, and maintaining robust automated attribution mechanisms to preserve developer productivity.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.