Common Android memory issues

Tips & tricks

These tips can help reduce the memory footprint during your Android builds.

Emulators

They don't work reliably within the cloud environment regardless of memory configuration. We have some workarounds available here

Java

Start with limiting the Java heap size. There are several envars to do this.

  • _JAVA_OPTIONS is for Oracle JVM which is most likely the one you're using. If your config doesn't have many services images, give a hefty amount of heap freedom for testing.
  • _JAVA_OPTIONS: -Xmx3g tells Java the heap size cannot grow larger than 3GB which is 75% of the default medium resource class. If there are many other services running in the job, you might want to go with 2GB which leaves more room for other services.

Gradle

  1. Make sure they are running Gradle with --no-daemon. The daemon runs by default and is helpful when running locally as it speeds up recompilation.
    This is not needed in continuous integration since every compilation is fresh. It takes up more memory for no benefit. You can pass --no-daemon directly to the gradlew script or set org.gradle.daemon=false in your gradle.properties file.

  2. By default, Gradle will spawn a worker process for every available CPU. CircleCI Cloud will erroneously report within to jobs that they have 32 available. With the default resource class, they actually only have access to 2. Setting --max-workers=2 will limit Gradle from spawning too many workers.

    Many options for configuring Gradle can also be applied through the gradle file:
    testOptions {
    unitTests.all {
      maxHeapSize = "1024m"
    }
    }

Zygote

There's a race condition in the Android 10 emulator image init process that sometimes causes Zygote (the Android OS process that spawns apps) to be started with the incorrect arguments, ultimately causing apps to be started with a tiny Java heap (-Xmx16m). If you then try to run espresso tests on a running emulator which has Zygote running with the wrong heap size of 16m, OutOfMemoryExceptions will be thrown, causing the espresso orchestrator process to crash (for more details about this issue, check out this article here).

The simplest way to resolve this issue is to call adb root && sleep 5 && adb setprop ctl.restart zygote as a post-emulator-run-step. This will kill any running logging sessions, so you will have to manually restart your own logging as another post-emulator-run-step, but otherwise this will cause Zygote to restart with the correct arguments and alleviate OOM issues. Please be aware this will not work on "google_play" image flavors as it is not possible to adb root in those images.

Kotlin

By default, Kotlin will spawn a daemon alongside Gradle. This is similar to the Gradle daemon and isn't necessary for CI.
Kotlin can be told to run within the same memory allocation as Gradle by setting GRADLE_OPTS=-Dkotlin.compiler.execution.strategy=in-process

 

Note: If you're still hitting memory issues, consider increasing the resource class.

Was this article helpful?
13 out of 20 found this helpful

Comments

0 comments

Article is closed for comments.