“无法创建新本地线程”不是栈溢出错误,而是操作系统拒绝分配线程资源,根源在于线程总数或线程栈总占用超限;需通过ps、ulimit、jstack等命令确认线程数是否真实爆满,并重点排查长连接场景下的线程滥用(如new thread、newcachedthreadpool、eventloopgroup配置不当)及连接泄漏问题。

“无法创建新本地线程”(java.lang.OutOfMemoryError: unable to create new native thread)在长连接场景中很常见,但它**不是栈溢出错误(StackOverflowError)**,而是操作系统层面拒绝分配线程资源的结果。真正发生 StackOverflowError 时,错误信息明确含 “StackOverflowError”,且只影响单个线程;而 “unable to create new native thread” 是 JVM 尝试创建新线程失败,根源在线程总数或线程栈总占用超限。
确认是不是线程数真爆了
别急着调参数,先看事实:
- 查当前 Java 进程线程数:
ps -T -p <pid> | wc -l</pid>(结果减 1 才是真实线程数) - 查系统对单进程的线程上限:
cat /proc/<pid>/limits | grep "Max processes"</pid>或ulimit -u - 查全局线程上限:
cat /proc/sys/kernel/threads-max - 用
jstack <pid></pid>统计活跃线程数:jstack <pid> | grep "java.lang.Thread.State" | wc -l</pid>
重点排查长连接场景的线程滥用模式
长连接(如 WebSocket、Netty、HTTP/2、MQTT 客户端)本身不直接导致该错,但配套的线程使用方式极易踩坑:
- 每个连接都
new Thread().start()—— 连接数 5000,线程就 5000+ - 使用
Executors.newCachedThreadPool()处理长连接 I/O 事件 —— 空闲 60 秒才回收,突发流量下线程爆炸式增长 - Netty 中 EventLoopGroup 配置不合理:比如
new NioEventLoopGroup(0)(0 表示用 CPU 核数 × 2),但在容器里 CPU 被限制却未感知,实际创建过多线程 - 连接未正确关闭 + 心跳/重连逻辑缺陷 → 连接泄漏 → 线程池持续扩容 → 最终触顶
针对性调优和修复手段
从 JVM 和 OS 两个层面协同解决:
-
减小单线程栈大小:长连接通常不深递归,无需默认 1MB 栈。加 JVM 参数
-Xss256k或-Xss192k,可使同等内存支持 4–5 倍线程数 -
限制线程池最大并发数:禁用无界线程池;用
ThreadPoolExecutor显式指定corePoolSize和maximumPoolSize,搭配有界队列(如ArrayBlockingQueue) -
调整 OS 线程限制:
- 临时提高:
ulimit -u 65535 - 永久生效(Linux):在
/etc/security/limits.d/90-nproc.conf中添加* soft nproc 65535和* hard nproc 65535 - 增大内核线程上限:
echo 131072 > /proc/sys/kernel/threads-max,并写入/etc/sysctl.conf
- 临时提高:
-
检查连接生命周期管理:确认 Netty 的
Channel.close()、WebSocket 的session.close()是否被调用;使用连接池(如 HikariCP 对 DB,或自研连接池对长连接)复用通道,避免频繁建连
辅助诊断技巧
线上快速定位问题源头:
- 抓取线程快照对比:
jstack <pid> > jstack_$(date +%s).txt</pid>,间隔 30 秒再抓一次,比对新增线程的堆栈,看是否集中在某个 handler 或 retry 逻辑 - 监控线程数趋势:通过 Micrometer + Prometheus 暴露
jvm.threads.live,观察是否随连接数线性增长 - 检查 JNI 或 native 库:某些网络库(如 tcnative、netty-transport-native-epoll)若版本不匹配或配置异常,也可能隐式创建额外线程











