Spring-RabbitMQ内存泄漏排查:PublisherCallbackChannelImpl listeners堆积求助
问题分析与解决方案
原因判定:确实是内存泄漏问题
你的场景完全符合内存泄漏特征:
- 采用原型模式的
RabbitTemplate每次发送消息都会生成新实例,只要你的应用启用了**发布确认(publisher confirms)或消息退回(publisher returns)**功能(哪怕只是代码中设置了confirmCallback/returnCallback),每个新实例都会在PublisherCallbackChannelImpl的listeners集合里注册一个回调对象。 - 在你使用的2.2.17.RELEASE版本中,
PublisherCallbackChannelImpl没有实现listeners的主动移除逻辑,这些回调对象会被listeners强引用——哪怕原型RabbitTemplate实例本身被丢弃,回调也无法被GC回收。日积月累,listeners会膨胀到占用大量内存,直接导致性能下降。
解决办法
1. 切换为单例RabbitTemplate(优先推荐)
RabbitTemplate是线程安全的,Spring官方明确建议使用单例模式。直接移除原型作用域的配置(比如@Scope("prototype"))即可。
如果之前用原型是为了动态修改exchange、routingKey这类参数,完全可以在每次发送消息前调用setExchange()、setRoutingKey()来调整,不需要新建实例。
2. 必须用原型时手动清理回调
如果业务场景确实需要保留原型模式,一定要在RabbitTemplate实例用完后做清理:
- 手动调用
setConfirmCallback(null)和setReturnCallback(null),触发回调从listeners中移除的逻辑; - 或者直接调用
destroy()方法,这个方法会自动清理所有已注册的回调,避免内存堆积。
3. 升级spring-rabbit版本
后续的spring-rabbit版本(2.3.x及以上)修复了这个内存泄漏问题,优化了回调的注册与回收机制。如果条件允许,可以升级Spring Boot版本(比如到2.4.x及以上),对应的spring-rabbit会同步升级,从根源上解决这个问题。
内容的提问来源于stack exchange,提问作者Jason Wen
相关产品推荐
相关产品推荐

