You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Spring Boot应用启动时出现java.lang.OutOfMemoryError: unable to create new native thread问题求助

老哥,我来帮你把这个问题捋清楚——你遇到的unable to create new native thread根本不是JVM堆内存不够,而是系统或者进程的线程数达到上限了,结合你用Jetty的报错栈,咱们一步步排查解决:

一、先搞懂问题本质

这个错误的核心是:操作系统已经没法给你的Spring Boot进程分配新的原生线程了,和JVM堆/栈内存不足是两回事。你的报错栈里明确是Jetty的QueuedThreadPool在尝试启动线程时失败,说明Jetty的线程池配置或者系统线程限制出了问题。

二、排查步骤

1. 先查系统层面的线程限制

  • 查看当前用户单个进程的线程上限:执行 ulimit -u,比如默认可能是1024,意思是你这个用户的单个进程最多能创建1024个线程。
  • 查看系统全局的线程总数上限:执行 cat /proc/sys/kernel/threads-max,这是整个系统能承载的线程总数。
  • 查看你的Spring Boot进程当前用了多少线程:先通过ps aux | grep java找到进程ID,再执行 ps -Lf <你的进程ID>,看NLWP列的总和就是当前进程的线程数,对比前面的限制值,判断是不是快到顶了。

2. 检查Jetty的线程池配置

你的报错来自Jetty的线程池,先看Spring Boot里的Jetty配置:

  • 默认情况下,Jetty的线程池是最小8个、最大200个线程。如果应用并发量很高,或者有其他组件额外创建线程,很容易触达系统的线程限制。
  • 你可以打开application.ymlapplication.properties查看配置,比如yaml格式的配置示例:
    server.jetty.threads.min=8
    server.jetty.threads.max=200
    server.jetty.threads.queue-size=1000
    
    重点看max值是不是设得过高,超过了系统给进程的线程上限。

3. 排查应用自身的线程创建逻辑

  • 有没有代码里手动new Thread()且没回收?比如每次请求都创建新线程,这种很快就会把线程耗尽。
  • 有没有自定义线程池配置不合理?比如核心线程数设得太大,或者拒绝策略用了CallerRunsPolicy之外的策略,导致并发高时不断创建新线程。
  • 第三方组件(比如Redis客户端、消息队列、定时任务框架)是不是偷偷创建了大量线程?比如有些MQ客户端默认会开很多消费线程,要去查组件的配置。

4. 检查JVM的线程栈大小

每个线程都会占用一块栈内存,JVM的-Xss参数控制这个大小,默认一般是1MB左右。如果-Xss设得太大,每个线程占的内存就多,系统的虚拟内存可能不够分配,导致没法创建新线程。你可以用jinfo <你的进程ID>查看当前的-Xss配置。

三、解决方法

1. 调整系统线程限制(如果有权限)

  • 临时调整:执行 ulimit -u 4096(把上限改成4096,根据实际情况调整),但重启终端就会失效。
  • 永久调整:修改/etc/security/limits.conf,添加两行:
    your_username soft nproc 4096
    your_username hard nproc 4096
    
    替换成你的用户名,重启系统或重新登录后生效。
  • 调整系统全局线程数:临时修改执行 echo 10000 > /proc/sys/kernel/threads-max;永久修改则在/etc/sysctl.conf里添加kernel.threads-max=10000,再执行sysctl -p生效。

2. 优化Jetty线程池配置

  • 根据应用类型调整max值:如果是CPU密集型应用,max设成CPU核心数+1即可;如果是IO密集型,可以适当调大,但别超过系统的线程限制。
  • 调整队列大小:把server.jetty.threads.queue-size设大一点,比如1000,这样当线程数达到max时,请求会进入队列排队,而不是让Jetty尝试创建新线程。

3. 优化应用的线程使用

  • 所有线程创建都用线程池复用,绝对禁止手动new Thread()这种操作。
  • 检查第三方组件的线程配置:比如Redis客户端的连接池线程数、MQ消费线程数、Quartz的定时任务线程池,都要设合理的值,别贪多。
  • 排查线程泄漏:用jstack <你的进程ID>导出线程栈,看看有没有大量处于RUNNABLE或WAITING状态的异常线程,比如线程池没关闭、线程一直在运行没结束。

4. 调整JVM线程栈大小

如果你的应用没有深度递归调用,可以适当调小-Xss,比如改成-Xss256k,这样每个线程占用的内存减少,系统就能分配更多线程。但要注意,调小后如果有深度递归,可能会触发StackOverflowError,所以要测试验证后再上线。

内容的提问来源于stack exchange,提问作者sanjay Chauhan

火山引擎 最新活动