该错误是操作系统拒绝分配线程资源导致的OutOfMemoryError: unable to create new native thread,主因是JVM创建线程时超出系统对进程线程数的限制(如ulimit -u或/proc/sys/kernel/threads-max),而非堆内存不足;每个Java线程需占用约1MB栈空间及内核资源,线程数过多会耗尽这些资源。

这个错误不是堆内存不够,而是操作系统拒绝分配线程资源。JVM 每创建一个线程,就要向系统申请一块栈空间(Linux 默认约 1MB),还要占用线程描述符、调度结构等内核资源。一旦超出限制,就会直接报 java.lang.OutOfMemoryError: unable to create new native thread。
先确认是不是真爆了线程数
别改代码,先看事实:
- 查当前 Java 进程的线程总数:
ps -T -p <pid> | wc -l(结果减 1 才是真实线程数) - 查该进程允许的最大线程数:
cat /proc/<pid>/limits | grep "Max processes" - 对比系统级上限:
cat /proc/sys/kernel/threads-max和ulimit -u(注意:不同用户可能有不同限制) - 用
jstack <pid>输出里统计"java.lang.Thread.State"出现次数,比 pstack 更准、更轻量
重点盯住线程池和线程创建方式
多数问题出在“不加节制地造线程”:
- 避免
Executors.newCachedThreadPool():它用 SynchronousQueue + 无上限线程数,流量一冲就几千上万线程 - 不用
new Thread().start()处理请求或循环任务,尤其在高并发或远程调用场景下 - 自定义
ThreadPoolExecutor时,必须显式指定:corePoolSize、maximumPoolSize、有界队列(如ArrayBlockingQueue(1000))、以及拒绝策略(RejectedExecutionHandler) - 检查是否在 for 循环里反复
submit()或execute(),且没做限流或批量控制
看线程 dump 抓典型模式
jstack -l <pid> 输出里,光看总数不够,要找异常分布:
- 大量线程卡在
TIMED_WAITING (parking),且堆在Unsafe.park—— 很可能是线程池空闲线程未回收,或阻塞在锁/IO 上 - 一堆名字相似的线程(如
pool-1-thread-xxx)持续增长,说明线程池配置失控 - 出现大量
WAITING on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject,可能队列积压严重,线程被阻塞在 take() 上 - 留意是否有 JNI 调用后未释放线程,或第三方 SDK(如某些数据库驱动、监控 agent)悄悄启了后台线程
系统和 JVM 层面可调的边界
临时缓解或长期优化都要兼顾:
- 降低单线程栈大小:
-Xss256k(默认 1MB),能显著提升理论最大线程数 - 调大用户级线程限制:
ulimit -u 8192(需写入/etc/security/limits.conf持久化) - K8s 环境注意容器
pids-limit,不能只调宿主机 ulimit - 公式估算可用线程数:
(进程虚拟内存上限 − JVM 堆内存 − OS 预留内存) ÷ -Xss,帮助判断是资源不足还是纯配置问题

















