升级Spring Boot与Java版本后出现IllegalReferenceCountException异常
问题诊断与解决方案
问题核心分析
你遇到的io.netty.util.IllegalReferenceCountException并非单纯的缓冲区容量不足问题——尽管调大缓冲区能降低错误频率,但根源是Netty ByteBuf的引用计数被重复释放,这种问题在高并发负载场景下会间歇性触发,通常和Spring Boot 3.x + Java 17升级后Reactor Netty的并发处理逻辑变化有关。
针对性解决方案
1. 排查WebClient响应处理的重复消费问题
检查所有WebClient调用的响应处理逻辑,是否存在重复消费响应体的情况:
- 同时使用
bodyToMono()/bodyToFlux()和直接读取response.content() - 在
doOnNext()/doOnSuccess()等回调中再次读取已被消费的响应体 - 手动操作ByteBuf后未正确管理引用计数
错误示例:
webClient.get() .uri("/api/data") .retrieve() .bodyToMono(MyData.class) .doOnNext(data -> { // 此处再次尝试读取响应体,导致重复释放ByteBuf clientResponse.content().subscribe(...); });
2. 升级Reactor Netty及Spring Boot补丁版本
Spring Boot 3.0.3配套的Reactor Netty版本存在部分并发场景下的引用计数bug,建议:
- 升级Spring Boot至3.0.x系列的最新补丁版本(如3.0.10+),该版本会同步修复Reactor Netty中的相关问题
- 若无法整体升级Spring Boot,可单独指定Reactor Netty版本至
1.1.10+(适配Spring Boot 3.0.x)
3. 手动管理ByteBuf引用计数(必要时)
如果业务逻辑需要多次处理响应体,需手动维护ByteBuf的引用计数:
webClient.get() .uri("/api/data") .exchangeToMono(response -> { return response.content() .map(buf -> { // 增加引用计数,确保多次使用不会提前释放 buf.retain(); return buf; }) .flatMap(buf -> { // 第一次处理 Mono<Result1> result1 = processFirst(buf); // 再次增加引用计数 buf.retain(); // 第二次处理 Mono<Result2> result2 = processSecond(buf); // 合并结果后统一释放ByteBuf return result1.zipWith(result2) .doFinally(signalType -> buf.release(2)); // 释放两次retain的计数 }); });
4. 避免在Netty线程中执行阻塞操作
负载测试下,若WebClient的响应处理中存在block()/Thread.sleep()等阻塞操作,会导致Netty EventLoop线程阻塞,进而引发引用计数异常。确保所有响应处理逻辑都使用Reactor的非阻塞API(如flatMap()/map()替代阻塞调用)。
内容的提问来源于stack exchange,提问作者Ayesha Niazi
相关产品推荐
相关产品推荐

