跳转到主内容
websoft网络软件专家 - 深耕网络技术,打造实用软件!

如何排查长连接场景导致的无法创建新本地线程的栈空间溢出错误

“无法创建新本地线程”不是栈溢出错误,而是操作系统拒绝分配线程资源,根源在于线程总数或线程栈总占用超限;需通过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 | wc -l
(结果减 1 才是真实线程数) 查系统对单进程的线程上限:
cat /proc//limits | grep "Max processes"
或
ulimit -u
查全局线程上限:
cat /proc/sys/kernel/threads-max
用
jstack
统计活跃线程数:
jstack | grep "java.lang.Thread.State" | wc -l
重点排查长连接场景的线程滥用模式 长连接(如 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 > jstack_$(date +%s).txt
,间隔 30 秒再抓一次,比对新增线程的堆栈,看是否集中在某个 handler 或 retry 逻辑 监控线程数趋势:通过 Micrometer + Prometheus 暴露
jvm.threads.live
,观察是否随连接数线性增长 检查 JNI 或 native 库:某些网络库(如 tcnative、netty-transport-native-epoll)若版本不匹配或配置异常,也可能隐式创建额外线程

相关文章