linux线程创建失败需逐层排查:先用ps -elf | wc -l、ulimit -u、cat /proc/sys/kernel/threads-max和pid_max对比定位瓶颈,90%问题卡在用户级ulimit -u限制,应优先调大并适配systemd或容器环境。

查清到底是哪一层在卡线程创建
Java 或 Go 应用报 java.lang.OutOfMemoryError: unable to create new native thread,但堆内存充足,不能直接认定是内核级问题。Linux 线程数限制是三层叠加的:ulimit -u(用户级)、/proc/sys/kernel/threads-max(内核级)、/proc/sys/kernel/pid_max(PID 总量天花板)。任一层触顶都会失败。
快速定位方法:
-
ps -eLf | wc -l查当前总线程数 -
cat /proc/sys/kernel/threads-max和cat /proc/sys/kernel/pid_max对比上面结果,看是否接近 -
ulimit -u查当前 shell 的用户级上限(普通用户常为 1024) -
prlimit -n $PID查具体进程实际继承的nproc值(注意:systemd 服务默认不读limits.conf)
改 ulimit -u 是最常见且最该优先做的
90% 的线程创建失败其实卡在这一层——因为 ulimit -u 同时约束进程 + 线程总数(Linux 中线程即轻量进程),而 Java 进程启动时直接继承 shell 的该限制。
临时生效(当前终端及子进程):ulimit -u 65535
永久生效(需新登录会话):
编辑 /etc/security/limits.d/90-nproc.conf,写入:
* soft nproc 65535 * hard nproc 65535
注意:
- systemd 服务不走 PAM limits 流程,必须额外在 service 文件中加
LimitNPROC=65535,或全局设DefaultLimitNPROC=65535在/etc/systemd/system.conf - Docker 容器需显式传参:
docker run --ulimit nproc=65535:65535 - Kubernetes 需在 Pod
securityContext中配limits.nproc
调 /proc/sys/kernel/threads-max 要同步检查 pid_max
这个值是内核能支撑的全局线程总数,不是 per-user,也不是 per-cgroup。默认 ≈ mem_total_kb / (4 * page_size),物理内存越大初始值越高,但不是无限涨。
关键约束:pid_max 必须 ≥ threads-max,否则写入会静默截断,报 Invalid argument。
操作步骤:
- 先查现状:
cat /proc/sys/kernel/pid_max;若它小于目标值,先调大:sysctl -w kernel.pid_max=4194304 - 再设 threads-max:
echo 2097152 > /proc/sys/kernel/threads-max(需 root) - 永久配置:写入
/etc/sysctl.d/99-thread-limit.conf,内容为kernel.threads-max = 2097152,然后运行sysctl --system
注意:threads-max 不建议设超过 pid_max 的 80%,且 x86_64 下 pid_max 上限约 4194304。
容器里改 threads-max 仅对当前 namespace 生效
如果你在 Docker 或 Kubernetes 里跑高并发服务,别只改宿主机——容器默认有自己的 PID namespace,/proc/sys/kernel/threads-max 是隔离的。
验证方式:进容器执行 cat /proc/sys/kernel/threads-max,很可能还是宿主机默认值(比如 65536),而非你期望的 2097152。
解决办法:
- Docker:启动时加
--sysctl kernel.threads-max=2097152(要求 Docker 20.10+ 且容器未禁用 sysctl) - Kubernetes:在 Pod spec 的
securityContext.sysctls中添加{"name": "kernel.threads-max", "value": "2097152"},并确保节点允许该 sysctl(unsafe-sysctls白名单) - 注意:容器内修改后,
pid_max也得同步调,否则依然无效
真正容易被忽略的是:同一台机器上多个容器都设了高 threads-max,但没调宿主机的 pid_max,最终所有容器共享的 PID 总池子还是会被耗尽。











