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