必须禁用new Thread().start(),因其绕过资源管控导致线程数暴增、触发OutOfMemoryError: unable to create new native thread;应统一使用显式配置corePoolSize、maximumPoolSize、有界队列及拒绝策略的ThreadPoolExecutor。
直接用 new thread().start() 创建线程,本质上是绕过所有资源管控的“裸创建”。每次调用都会向操作系统申请一个原生线程(native thread),占用约 1mb 栈空间 + 内核线程描述符等资源。一旦并发量上来或循环中反复执行,线程数会指数级增长,很快触达系统上限(如 ulimit -u 或 /proc/sys/kernel/threads-max),触发 java.lang.outofmemoryerror: unable to create new native thread —— 这不是 jvm 堆满了,而是操作系统说“我线程资源没了”,直接拒绝分配。
立刻停掉裸线程创建行为
这不是性能优化问题,是架构级错误。必须从代码层面根除:
- 全局搜索项目中所有
new Thread(、Thread t = new Thread(、new Thread(...).start()的写法,逐个替换 - 禁止在请求处理链路(如 Controller、Service 方法)、定时任务、IO 回调、循环体中出现该模式
- 特别警惕“为每个请求起一个线程”“为每个文件起一个线程”“for 循环里每轮 new 一个线程”这类典型反模式
统一收口到受控线程池
用 ThreadPoolExecutor 替代,且参数必须显式限定:
- corePoolSize:设为 CPU 核数 +1~2(CPU 密集型)或 2×核数(IO 密集型),避免空闲线程长期驻留
- maximumPoolSize:建议 ≤200,防止突发流量打爆系统;不要设 Integer.MAX_VALUE
-
workQueue:必须用有界队列,例如
new ArrayBlockingQueue(1000);禁用LinkedBlockingQueue(无界)和SynchronousQueue(易导致线程爆炸) -
RejectedExecutionHandler:至少配置
CallerRunsPolicy,让调用方自己承担压力,而不是静默丢任务或新建线程
检查并回收已有线程泄漏点
裸线程虽已禁用,但历史代码可能遗留“只建不关”的线程池或匿名线程:
- 查定时任务类、工具类、SDK 初始化代码,确认线程池是否声明为
static final,是否重复初始化 - 查
new Thread(...)后是否漏了join()或未捕获异常导致线程提前终止但资源未释放 - 用
jstack <pid></pid>检查线程名是否大量出现Thread-xxx(非 pool 命名),这是裸线程残留的明显信号 - 确认第三方组件(如旧版 Druid、某些监控 agent)是否悄悄启动后台线程,必要时升级或配置关闭
系统与 JVM 层面辅助加固
作为兜底手段,降低单线程开销、抬高系统限制:
- JVM 启动加
-Xss256k:将默认 1MB 线程栈压到 256KB,理论线程数可提升近 4 倍 - 调整用户级限制:
ulimit -u 8192(写入/etc/security/limits.conf持久化) - 确认部署环境为 64 位系统:32 位进程地址空间受限(如 Windows 下仅约 2GB),天然制约线程数量
- 避免给 JVM 分配过大堆内存(如
-Xmx8g):公式线程数 ≈ (进程总内存 − JVM 堆 − OS 预留) ÷ 线程栈,堆越大,能创建的线程越少


















