先查 ulimit -u,90% 的线程创建失败源于用户级限制而非内核参数;linux 中线程与进程共用 pid,ulimit -u 限制的是二者总和,java 进程启动时直接继承该值,需同步调大 ulimit -u、pid_max 和 threads-max 并验证 prlimit -n $pid 生效。

线程创建失败,先查 ulimit -u 而不是直接改内核参数
90% 的 java.lang.OutOfMemoryError: unable to create new native thread 不是内核限制导致的,而是用户级 ulimit -u 卡住了。Linux 中线程和进程共用 PID,ulimit -u 限制的是“进程 + 线程”总数,Java 进程启动时直接继承 shell 的该值。
常见错误现象:ps -eLf | wc -l 查出当前线程数接近 4096,而 ulimit -u 输出也是 4096 —— 这就是瓶颈。
- 临时生效:运行
ulimit -u 65535(当前终端及所有子进程立即生效) - 永久生效(新登录会话):写入
/etc/security/limits.d/90-nproc.conf,内容为:* soft nproc 65535* hard nproc 65535 - systemd 服务不读
limits.conf,必须在 service 文件里加LimitNPROC=65535,或全局设DefaultLimitNPROC=65535在/etc/systemd/system.conf - Docker 容器需显式传参:
docker run --ulimit nproc=65535:65535
改 /proc/sys/kernel/threads-max 前必须确认 pid_max ≥ 它
/proc/sys/kernel/threads-max 是内核能支撑的全局线程总数,但它的值不能超过 /proc/sys/kernel/pid_max,否则写入会静默失败,报 Invalid argument。
默认值 ≈ 总内存 KB / (4 × 页面大小),物理内存越大初始值越高,但不是无限涨;x86_64 下 pid_max 上限约 4194304,threads-max 不应显著超过它。
- 临时修改(重启失效):
echo 2097152 > /proc/sys/kernel/threads-max(需 root 权限) - 检查依赖:
cat /proc/sys/kernel/pid_max,若小于目标值,先调大:sysctl -w kernel.pid_max=4194304 - 永久配置:写入
/etc/sysctl.d/99-thread-limit.conf,内容为kernel.threads-max = 2097152,再执行sysctl --system - RHEL/CentOS 7+ 可能需额外
systemctl restart systemd-sysctl
容器环境里 threads-max 修改只对当前 namespace 生效
在 Docker 或 Kubernetes 中,/proc/sys/kernel/threads-max 默认继承宿主机,但修改操作仅作用于当前容器的 PID namespace —— 宿主机和其他容器不受影响。
Linux AI 服务器 — 将 Linux 服务器转换为本地 AI 推理集群。使用 systemd、NVIDIA CUDA 实现无头 Linux AI,零 GUI 开销。
不过要注意:如果容器启动时挂载了 procfs 且禁用了 write 权限(如 --read-only 或 securityContext readOnlyRootFilesystem=true),写入 /proc/sys/... 会直接失败。
- Docker 启动时可通过
--sysctl kernel.threads-max=65535传递(需 daemon 支持--allow-privileged) - Kubernetes 中需在 Pod 的
securityContext里配sysctls,且节点 kubelet 必须启用--allowed-unsafe-sysctls - 多数云厂商托管集群默认禁止 unsafe sysctls,此时只能靠调大
ulimit -u和合理控制应用线程池规模来规避
别忽略线程栈大小对实际可创建线程数的影响
即使 ulimit -u 和 threads-max 都调得很高,单个进程仍可能因虚拟内存耗尽而创建失败——因为每个线程默认占 8MB 栈空间(ulimit -s 输出值)。
例如:一个 Java 进程若设了 1000 个工作线程,仅栈就占约 8GB 虚拟内存;若 JVM 堆又占 4GB,很容易触到进程地址空间上限(尤其 32 位或受限容器)。
- 临时减小栈:
ulimit -s 1024(单位 KB,即 1MB) - Java 应用可在启动参数中指定:
-Xss256k(比系统级ulimit -s更精准) - 注意:过小的栈可能导致
StackOverflowError,尤其递归深或本地变量多的场景
实际调优时,ulimit -u、threads-max、pid_max、ulimit -s 这四者相互牵制,漏掉任意一层都可能让配置失效。最稳妥的做法是:先用 ps -eLf | wc -l 和 prlimit -n $PID 定位具体卡在哪一层,再逐层放开。










