Skip to content
Latchkey

Java "OutOfMemoryError: unable to create native thread" in CI

This OOM is not about heap - it means the OS refused to create another native thread. Each Java thread needs an OS thread and a native stack; the build hit a per-process thread/ulimit cap, ran out of native memory for stacks, or is leaking threads.

What this error means

A run or test fails with java.lang.OutOfMemoryError: unable to create new native thread, often during heavily parallel tests, thread-pool-creating code, or many forked workers. Heap usage may be low.

java
java.lang.OutOfMemoryError: unable to create new native thread
        at java.base/java.lang.Thread.start0(Native Method)
        at java.base/java.lang.Thread.start(Thread.java:809)
        at com.example.Pool.spawn(Pool.java:27)

Common causes

Thread/process limit reached

The ulimit -u (max user processes) or a container thread cap is hit because too many threads exist at once - from leaks or over-parallel execution.

Native memory exhausted for stacks

Each thread reserves stack space; with a huge thread count or large -Xss, the process runs out of native memory to back new stacks.

How to fix it

Cap concurrency and bound thread pools

Reduce parallel forks and use bounded pools so the process does not exceed the thread limit.

build.gradle
// Gradle: fewer concurrent test workers
test { maxParallelForks = 2 }
// App code: bounded pool instead of unbounded thread creation
ExecutorService pool = Executors.newFixedThreadPool(16);

Raise limits or fix the leak

  1. Increase ulimit -u / --pids-limit for the job if the cap is genuinely too low.
  2. Take a thread dump (jstack) to find leaked or runaway threads.
  3. Reuse executors instead of creating threads per task.

How to prevent it

  • Use bounded, reusable thread pools, cap test parallelism to the runner, and right-size process/thread ulimits so native-thread creation never starves.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →