Prototype泄漏主因是开发者误将有状态对象交由Spring管理且未管控生命周期,导致对象堆积、引用滞留、GC不及时而OOM。
这个问题本质不是“prototype 本身导致泄漏”,而是把有状态对象错误地交给 spring 管理,又没管好它的生命周期——容器不销毁原型 bean,但开发者误以为它会自动清理,结果对象堆积、引用滞留、gc 回收不及时,最终 oom。
确认是否真为 Prototype 引发的泄漏
Spring 的 prototype Bean 本身不被容器管理销毁:创建后就变成 JVM 普通对象,靠 GC 回收。所以泄漏往往不是因为“创建太多”,而是因为:
- Bean 内部持有大对象(如 200MB byte[])、长生命周期资源(未关闭的流、监听器、线程池);
- Bean 被意外强引用滞留(例如被静态集合缓存、被单例 Bean 长期持有、注册到全局事件总线未反注册);
- 高并发下实例暴增 + 单个实例存活时间过长(如 sleep 10 秒、同步阻塞、IO 等待),导致堆内存瞬时打满。
停止错误注入方式:别用 @Autowired 直接注入 prototype Bean
这是最常见陷阱:在单例 Service 中 @Autowired private MyPrototypeBean bean;,看似每次用新实例,实则只注入一次——后续调用永远用同一个实例,完全违背 prototype 语义,还掩盖了状态污染问题。
应改为按需获取:
- 通过
ApplicationContext.getBean(MyPrototypeBean.class)显式获取(注意避免在循环中高频调用且不释放); - 或使用
@Autowired private ObjectProvider<myprototypebean> provider;</myprototypebean>,再调provider.getObject(); - 对 Web 场景,优先考虑
@Scope("request")或@Scope("session"),由 Spring 自动绑定请求/会话生命周期并清理。
让原型 Bean 具备自清理能力
既然容器不管销毁,就得自己负责。关键点:
- 实现
DisposableBean接口或标注@PreDestroy方法,在方法内释放资源(关闭流、取消定时任务、注销监听器); - 避免在 prototype Bean 中使用静态集合、缓存、线程池等跨实例共享结构;
- 若必须缓存数据,用
WeakReference或SoftReference包装,便于 GC 回收; - 大对象(如临时 buffer)建议延迟初始化,用完立即置为
null,加速可达性判断。
监控与压测验证
上线前必须验证真实行为:
- 用
jmap -dump:format=b,file=heap.hprof <pid></pid>抓堆快照,用 MAT 查看MyPrototypeBean实例数量及 retained heap; - 模拟高并发请求(如 500 线程持续调用),观察 GC 日志是否频繁 Full GC、老年代是否持续增长;
- 检查是否有
Finalizer队列堆积(说明对象重写了finalize()且未及时执行),这会严重拖慢回收。















