unable to create new native thread 不是堆内存问题,而是操作系统拒绝分配线程资源,因线程栈、描述符等超系统限制(如 threads-max 或 RLIMIT_NPROC)。

为什么 unable to create new native thread 不是堆内存问题
这个错误和 OutOfMemoryError: Java heap space 完全不是一回事——它不反映堆不够,而是操作系统拒绝给 JVM 新的线程资源。JVM 每建一个线程,就要向 OS 申请一块栈空间(默认 Linux 下约 1MB),还要分配线程描述符、内核调度结构等。一旦超出系统限制(比如 /proc/sys/kernel/threads-max 或每个进程的 RLIMIT_NPROC),就会直接抛出这个异常。
常见诱因包括:
- 线程池未复用,每次请求都 new Thread().start()
- 使用了无界队列 + 无限扩容的线程池(如 Executors.newCachedThreadPool() 在高并发下疯狂创建)
- Native 层 JNI 调用泄漏了线程(较少见,但排查时不能忽略)
怎么快速确认是不是线程数真爆了
别急着改代码,先看事实:
- 查当前进程线程数:
ps -T -p <pid> | wc -l(注意第一行是标题,实际线程数减 1) - 对比系统上限:
cat /proc/<pid>/limits | grep "max user processes" - 看线程栈占用:
pstack <pid> | wc -l(粗略估算,大量线程时可能卡顿) - JVM 内部统计更准:用
jstack <pid>输出里数"java.lang.Thread.State"出现次数,或直接jstat -gc <pid>看NGCMN/NGCMX和堆无关,但jstat -t <pid>配合时间戳可观察线程增长趋势
Executors.newCachedThreadPool() 为什么在生产环境很危险
它底层用的是 SynchronousQueue + 无上限的 ThreadPoolExecutor,任务来一个就新建线程,空闲 60 秒才回收。流量突增时,几十万请求进来,线程数可能瞬间冲到几千甚至上万。
替代方案必须带明确边界:
立即学习“Java免费学习笔记(深入)”;
- 用
newFixedThreadPool(n):线程数固定,但队列默认无界,仍可能 OOM 堆 —— 所以要配自定义BlockingQueue,比如new ArrayBlockingQueue<Runnable>(1000) - 更推荐手动构造
ThreadPoolExecutor:显式指定corePoolSize、maximumPoolSize、workQueue和RejectedExecutionHandler - 避免在循环里调用
execute()前不加限流,尤其配合远程调用或文件读写时,响应慢会进一步堆积任务
线程 dump 里哪些线索最值得盯
jstack <pid> 输出里,光看线程总数不够,得抓模式:
- 大量线程处于
java.lang.Thread.State: TIMED_WAITING (parking)并堆在Unsafe.park—— 很可能是线程池空闲线程,正常;但如果数量远超maximumPoolSize,说明没生效 - 一堆线程卡在
java.net.SocketInputStream.socketRead0或类似 IO 阻塞点,且名字像pool-1-thread-1234:说明业务逻辑里有同步阻塞调用(如 HTTP 同步客户端、数据库长查询),线程被占住无法释放 - 出现大量
Finalizer、Reference Handler或名字含DestroyJavaVM的线程:基本可排除,它们是 JVM 自身线程 - 注意线程名:如果全是
Thread-12345这种匿名名,大概率是代码里裸写了new Thread(...).start()
线程数问题的根因往往不在“创建”动作本身,而在“不释放”——比如忘了 close() 数据库连接导致连接池耗尽,进而让业务线程卡死等待连接,新请求只能不断开新线程顶上。所以看到线程暴涨,先查下游依赖是否健康,比立刻调大 -Xss 或系统 limits 更有效。








