/proc/sys/kernel/threads-max 是决定系统能否新建线程的关键阈值,表示内核允许的总线程数上限,受内存总量和 pid_max 约束,全局共享且不等于 ulimit -u 或 pid_max;Java 报 unable to create new native thread 且堆内存充足时,大概率是该值已达上限。

直接读 /proc/sys/kernel/threads-max 是唯一可靠方式
这个文件的值就是内核当前允许创建的线程总数上限,它由内存总量和 pid_max 共同约束,不是估算值,也不依赖用户态工具解析。运行 cat /proc/sys/kernel/threads-max 就能得到准确数字。
注意:这个值是全局共享的,所有进程、所有用户共用;它不等于 ulimit -u(那是单用户进程+线程数限制),也不等于 pid_max(那是 PID 编号池上限,理论值大但不直接控制线程创建)。
常见误判场景:
- Java 报
java.lang.OutOfMemoryError: unable to create new native thread,而堆内存充足 → 很大概率是这个值卡死了 -
ulimit -u显示还有余量,但新线程就是 fork 不出来 → 说明threads-max已满,ulimit不起作用 -
pid_max很大(比如 4194304),但threads-max只有几十万 → 内核已静默截断,改threads-max前必须先确保pid_max≥ 它
为什么不能用 ulimit -u 或 sysctl kernel.pid_max 替代
ulimit -u 查的是 RLIMIT_NPROC,即单个用户能拥有的进程+线程总数。它只影响该用户的 fork() 调用,不影响其他用户,更不决定系统能否新建线程——哪怕所有用户都远未触顶,threads-max 一到,全系统都会失败。
pid_max 是 PID 号分配空间上限,x86_64 默认约 4194304。但它只是编号池,不是资源配额。内核实际线程容量受内存页数量限制,默认公式约为 total_memory_kb / (4 * page_size),且会取 min(计算值, pid_max)。所以只调大 pid_max 不改 threads-max,毫无意义。
临时修改 threads-max 的实操要点
需要 root 权限,且必须满足 pid_max ≥ 新设值,否则写入失败并报 Invalid argument。
临时生效命令:
Linux系统管理专家,覆盖12大模块:用户权限、SSH、存储、网络、systemd、防火墙、日志监控、备份恢复、TLS证书、Ansible、容器、IaC。提供配置、验证、加固、监控、备份、自动化、故障排查、回滚闭环。关键词:useradd、sudo、sshd_config、chmod、SEL...
echo 2097152 > /proc/sys/kernel/threads-max
验证是否成功:
- 立刻重查
cat /proc/sys/kernel/threads-max确认数值已变 - 检查
cat /proc/sys/kernel/pid_max是否 ≥ 新值 - 观察 Java 进程是否不再报
unable to create new native thread
该操作即时生效,无需重启服务或内核,已运行线程不受影响。
永久配置要写进 sysctl.d 而非 limits.conf
/etc/security/limits.conf 控制的是用户级 ulimit -u,对 threads-max 完全无效。永久修改必须走 sysctl 机制:
在 /etc/sysctl.d/99-threads-max.conf 中写入:
kernel.threads-max = 2097152
然后执行:
sysctl --system
这样重启后仍保持。别漏掉 --system,只用 sysctl -p 可能不加载 sysctl.d 下的新文件。
真正关键的不是“怎么改”,而是理解:这个数字一旦被耗尽,任何进程(包括 ps 自身)都无法再 clone() 出新线程——连诊断命令都可能启动失败。所以监控时务必盯住它与当前线程数的比值,而不是只看绝对剩余。

















