DevFix
Java已验证

OutOfMemoryError: unable to create new native thread 排查与解决

@debug_master更新于 3 天前阅读 6 min0

一句话先说结论:这个报错和 Java 堆没关系。它说的是 JVM 找操作系统申请原生线程失败,原因要么是线程数顶到 OS 上限,要么是线程栈把 native 内存吃空了。先查 ulimit -u 和线程数,再决定调大限制还是减线程。

背景

Java 的每个线程,背后都对应一个操作系统原生线程。原生线程的开销不进 Java 堆,走的是 native 内存。

线程栈是 native 内存的大头。64 位 JVM 默认一个线程栈约 1 MB。堆可能还有一半空着,线程数一多,native 内存先扛不住。

所以这个报错经常出现在监控显示「内存还很充足」的时候,容易让人一头雾水。

现象

线程池或压测脚本不停开线程,跑到某一刻报错:

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)

堆栈落在 Thread.start0,这是开原生线程的入口。注意它和 OutOfMemoryError: Java heap space 是两码事,后者才是堆满。

根因分析

两类原因,按频率排。

1. 线程数撞上操作系统的上限

Linux 把进程和线程当同一种东西管。下面的限制直接影响能开多少线程:

  • ulimit -u:每个用户能开的进程/线程数,常见默认是 1024。
  • kernel.threads-max:系统级线程总数上限。
  • kernel.pid_max:最大 PID,也间接卡住线程数。
  • 容器里的 cgroup pids 限制。
  • systemd 管理的服务,另有一个 UserTasksMax

2. native 内存被线程栈耗尽

每个线程栈默认约 1 MB。开 1 万个线程,光栈就要约 10 GB native 内存。线程多,或者 -Xss 设太大,都会把 native 内存耗光。

排查步骤

先确认是哪种原因。

看用户线程上限:

ulimit -u

看系统级参数:

cat /proc/sys/kernel/threads-max
cat /proc/sys/kernel/pid_max
cat /proc/sys/vm/max_map_count

数一下当前进程开了多少线程:

jstack <pid> | grep "java.lang.Thread.State" | wc -l

线程数贴近限制,基本就定位了。

解决方案

按根因对症下药。

调高用户线程上限

临时放开,让这个 shell 能开更多线程:

ulimit -u 8192

永久改,写 /etc/security/limits.conf

foo hard nproc 50000
foo soft nproc 50000
foo hard nofile 60000
foo soft nofile 60000

调内核参数

系统级上限不够时:

sysctl -w kernel.threads-max=100000
sysctl -w kernel.pid_max=400000
sysctl -w vm.max_map_count=500000

写进 /etc/sysctl.conf 才能重启后生效。

systemd 服务改 UserTasksMax

用 systemd 拉起的服务,检查 /etc/systemd/system.conf

UserTasksMax=60000

改完执行 systemctl daemon-reload

从根上减线程

调限制是治标,治本是少开线程。

  • 把无界的 newCachedThreadPool 换成有界线程池。
  • 控制压测和并发度。
  • 真有大量并发,Java 21 的虚拟线程更合适,它不占原生线程。

减小线程栈

确需大量原生线程时,可以压缩单个栈:

java -Xss512k ...

默认 1 MB,降到 512 KB 能让同等 native 内存容纳更多线程。但栈太小有 StackOverflowError 风险,得实测。

思考过程

排查时最容易犯的错,是看到 OutOfMemoryError 就调大 -Xmx。这个错误不归 -Xmx 管,调了没用。

先分清「堆满」和「线程满」是两类问题。堆满看 Java heap space,线程满是 unable to create new native thread。前者加堆,后者查 OS 限制和线程数。

容器环境还多一层:宿主机 ulimit -u 放开了,但容器 cgroup 的 pids 限制没调,照样开不了线程。

小结

  • 这个报错是 OS 拒绝创建新原生线程,和 Java 堆无关。
  • 根因:线程数顶到 ulimit/内核/容器限制,或线程栈耗光 native 内存。
  • ulimit -u 加数线程定位,再决定调限制还是减线程。
  • 治本是少开线程,或上虚拟线程。
  • 别拿 -Xmx 去修,方向错了。

来源

最后更新于 2026-08-23

这篇帮到你了吗?

刚解决了一个棘手的报错?花两分钟记录下来,帮助下一个遇到同样问题的开发者。

贡献一条解法