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.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.
// 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
- Increase
ulimit -u/--pids-limitfor the job if the cap is genuinely too low. - Take a thread dump (
jstack) to find leaked or runaway threads. - 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.