该错误是系统线程数超限所致,需检查并调高ulimit -u(用户进程上限)、降低-Xss栈大小、禁用无界线程池、排查线程泄漏,并确认JVM启动前限制已生效。

Java 应用在 Linux 上运行时,若遇到 java.lang.OutOfMemoryError: unable to create new native thread 或大量文件读写失败(如 Too many open files),往往不是 JVM 堆内存不足,而是系统级资源限制(如最大进程数、文件描述符数)被触及。这些限制由 Linux 的 ulimit 控制,需在 Java 进程启动前正确配置,仅调大 JVM 参数(如 -Xmx)无法解决。
理解 ulimit 相关限制项
ulimit 是 shell 内置命令,用于查看或设置当前 shell 会话的资源限制。对 Java 进程最关键的是以下两项:
-
-u(max user processes):单个用户可创建的最大进程/线程总数(包括子进程、线程)。Java 中每个线程对应一个内核线程,超限会导致native thread创建失败。 -
-n(open files):单个进程可打开的最大文件描述符数(含 socket、普通文件、管道等)。Java NIO、HTTP 连接池、日志文件轮转等都会占用 fd,超限会抛出IOException: Too many open files。
注意:ulimit -u 限制的是“用户级总进程数”,不是单个 Java 进程的线程数;而 ulimit -n 是“每个进程”的限制,Java 进程自身受其约束。
临时修改(当前会话生效)
适用于调试或手动启动 Java 程序:
立即学习“Java免费学习笔记(深入)”;
Linux系统管理专家,覆盖12大模块:用户权限、SSH、存储、网络、systemd、防火墙、日志监控、备份恢复、TLS证书、Ansible、容器、IaC。提供配置、验证、加固、监控、备份、自动化、故障排查、回滚闭环。关键词:useradd、sudo、sshd_config、chmod、SEL...
- 查看当前限制:
ulimit -u和ulimit -n - 临时调高(仅对当前 shell 及其子进程有效):
ulimit -u 65535ulimit -n 65535 - 再启动 Java 程序:
java -jar myapp.jar—— 此时该 Java 进程将继承这些限制。
⚠️ 注意:必须在启动 Java 命令前执行 ulimit,且不能超过系统硬限制(hard limit)。可用 ulimit -Hu 和 ulimit -Hn 查看硬限制。
永久生效(推荐用于生产环境)
临时设置重启终端即失效,生产环境需持久化。主流方式是修改 /etc/security/limits.conf:
- 以 root 权限编辑:
sudo vim /etc/security/limits.conf - 添加如下行(假设 Java 服务用
appuser用户运行):
# <domain> <type> <item> <value>appuser soft nproc 65535appuser hard nproc 65535appuser soft nofile 65535appuser hard nofile 65535 - 保存后,新登录的 appuser 会话才会生效。若已存在会话,需重新登录或重启服务。
- 补充:某些发行版(如较新 systemd 系统)还需检查
/etc/systemd/system.conf或服务单元文件中是否覆盖了 limits(例如DefaultLimitNOFILE=65535),否则 limits.conf 可能不生效。
验证 Java 进程实际生效的限制
不要只信配置文件,务必验证运行中的 Java 进程是否真正获得预期限制:
- 查 Java 进程 PID:
ps -ef | grep java - 查看该进程的 limits:
cat /proc/<PID>/limits | grep -E "(Max processes|Max open files)" - 输出示例:
Max processes 65535 65535 processesMax open files 65535 65535 files - 若显示为较低值(如 1024、4096),说明配置未生效,需检查用户身份、PAM 配置(
/etc/pam.d/common-session是否含pam_limits.so)、systemd 覆盖等环节。
不复杂但容易忽略。关键就三点:明确要调哪两个值(-u 和 -n)、设对作用域(用户级 vs 进程级)、启动前确认生效。

















