What the message means
The JVM asked the operating system for a new thread and the request failed. On Linux that call is pthread_create. The JVM reports the failure as an OutOfMemoryError, but it is about the process, not about the heap.
The wording depends on the JDK. JDK 8 prints unable to create new native thread. JDK 11, 17 and 25 print unable to create native thread: possibly out of memory or process/resource limits reached. Both mean the same.
Two consequences: a heap dump will not show the cause, and a larger -Xmx can make the error more frequent, because the stacks of the threads live outside the heap and the heap takes memory from them.
Common causes
- A thread leak. Code that creates an executor, or a thread, on every call and never shuts it down. The thread count grows until the limit. This is the most common cause in applications that ran well for days.
- The user process limit.
ulimit -ucaps the threads of a user, and every thread counts as a process for this limit. - The container limit. Docker
--pids-limit, the Kubernetes pod PID limit orpids.maxin the cgroup. The limit is on the container, so other processes in it count too. - System limits.
kernel.threads-max,kernel.pid_maxandvm.max_map_count. - No native memory left. Each platform thread reserves a stack (
-Xss, 1 MB by default on Linux x64). With enough threads the stacks alone use the memory the container has.
Capture the evidence
First count the threads and compare with the limits. Run this on the same host or container as the JVM:
Thread count and limits
grep Threads /proc/<pid>/status ulimit -u cat /sys/fs/cgroup/pids.current /sys/fs/cgroup/pids.max
Threads: in /proc/<pid>/status is the number of threads of the process. Then take the dump. At the limit, jstack and jcmd can fail because they need the JVM to create a thread to answer. kill -3 <pid> does not: the JVM writes the thread dump to its own stdout, so read it from the console or the container log. More ways to capture one: how to take a thread dump.
Got the dump? Paste it and see which thread names pile up.
Analyze my dump nowDump with sensitive data? Anonymize it first with tm-anon.
What it looks like in a dump
A thread leak shows as thousands of threads with the same stack and a name that counts up. This is a real dump from our reproduction (JDK 25), where the code created a new executor per call. Each thread is idle, parked in the queue of its own executor:
One of the idle pool threads
"pool-1-thread-1" #26 [54084] prio=5 os_prio=0 cpu=0.00ms elapsed=4.99s tid=0x000000b3ef02c230 nid=54084 waiting on condition [0x000000b3eafee000] java.lang.Thread.State: WAITING (parking) at jdk.internal.misc.Unsafe.park(java.base@25.0.2/Native Method) - parking to wait for <0x000000060ec0b3a0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) at java.util.concurrent.locks.LockSupport.park(java.base@25.0.2/LockSupport.java:369) ... at java.util.concurrent.LinkedBlockingQueue.take(java.base@25.0.2/LinkedBlockingQueue.java:435) at java.util.concurrent.ThreadPoolExecutor.getTask(java.base@25.0.2/ThreadPoolExecutor.java:1016) at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@25.0.2/ThreadPoolExecutor.java:1076) at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@25.0.2/ThreadPoolExecutor.java:614) at java.lang.Thread.runWith(java.base@25.0.2/Thread.java:1487) at java.lang.Thread.run(java.base@25.0.2/Thread.java:1474)
The sign is in the names. Each one is thread-1 of a different pool, 150 pools in this dump:
Names in the dump
"pool-1-thread-1" "pool-2-thread-1" "pool-3-thread-1" ... "pool-150-thread-1"
ThreadMine counts the threads by the name prefix and reports the pattern:
Executor leak: 150 separate thread pools with default names
150 threads
150 threads named pool-N-thread-M across 150 pools. A new executor is probably created per call and never shut down.
By hand, count the threads by the name without the number and look for one that dominates. A name that repeats thousands of times is the leak.
How to fix it
Find the cause with the count and the dump, then fix that. Changing limits comes last.
Shut down what you create
Every executor you create needs shutdown(). From Java 19 ExecutorService is AutoCloseable, so try-with-resources does it:
try-with-resources (Java 19+)
try (ExecutorService executor = Executors.newFixedThreadPool(4)) {
executor.submit(task);
} // close() waits for the tasks and stops the threads (Java 19+)Share a bounded pool
Create the executor once, as a bean or a field, with a fixed size. Do not create one per request.
Name the threads
Pass a ThreadFactory that names the threads after what they do. A leak with a real name is found in one look at the dump, and pool-N-thread-M hides who created it.
Raise the limit, last
Raise ulimit -u, the container PID limit or the system limits only after you ruled out a leak. With a leak, a higher limit only delays the same error. If the count is high and legitimate, lower -Xss or reduce the pool sizes before raising anything.
More about reading a dump of a stuck application: Spring Boot stuck threads.
Frequently asked questions
Is "unable to create native thread" a heap problem?
No. The heap still has room. The JVM asked the operating system for a new thread (pthread_create on Linux) and the call failed. A heap dump will not help, and a larger -Xmx leaves less memory for thread stacks, so it can make the error more frequent.
What is the difference between "unable to create new native thread" and "unable to create native thread"?
Only the wording. JDK 8 prints "unable to create new native thread". JDK 11, 17 and 25 print "unable to create native thread: possibly out of memory or process/resource limits reached". The cause is the same, so the same fixes apply.
Why does jstack fail when this happens?
jstack and jcmd attach to the JVM through a helper thread, and at the limit the JVM may not be able to create it. kill -3 <pid> does not need to attach: the JVM prints the thread dump to its own stdout, so look in the console or the container log.
How many threads can a JVM handle?
There is no fixed number. The limit is whichever of these is reached first: the user process limit (ulimit -u), the container PID limit, the system limits (threads-max, pid_max, vm.max_map_count) and the native memory left for stacks, roughly the stack size (-Xss) times the number of threads. A healthy service rarely needs thousands of platform threads.
How do I see the thread limit of my container?
Inside the container, cat /sys/fs/cgroup/pids.max shows the limit and pids.current the current count (cgroup v2). Docker sets it with --pids-limit, and Kubernetes with the pod PID limit. If pids.current is close to pids.max, the container limit is what you hit.
Got the dump of the JVM at the limit?
Paste it and see which thread names pile up. No signup for the first dump.