Netty同步客户端抛出IllegalReferenceCountException问题排查求助
错误原因
java.util.concurrent.CompletionException: io.netty.util.IllegalReferenceCountException: refCnt: 0 本质是尝试访问已被Netty自动释放的对象。Netty基于引用计数管理ByteBuf等资源,当对象引用计数归0时,内存会被回收,此时再访问就会触发该异常。
代码中的核心问题
1. SimpleChannelInboundHandler的自动释放机制
客户端使用SimpleChannelInboundHandler<FullHttpResponse>处理响应,该Handler的特性是:在channelRead0方法执行完毕后,自动调用ReferenceCountUtil.release(response)释放传入的响应对象。
你在channelRead0中把response传给future.complete(response),后续在thenAccept回调里尝试读取response.content()时,response已经被Handler自动释放,引用计数变为0,因此触发异常。
2. 客户端同步逻辑阻塞导致资源提前释放
在sendHttpsRequest方法中,发送请求后调用了channel.closeFuture().sync(),该方法会阻塞直到通道关闭。而你在channelRead0里调用ctx.close(),通道关闭时会触发Netty的资源清理流程,进一步加速响应对象的释放,导致后续回调访问时对象已失效。
3. 请求ByteBuf的引用计数未正确处理
客户端main方法中,创建content ByteBuf后,通过request.content().writeBytes(content)写入请求体,但未调用content.release()释放该临时ByteBuf,存在内存泄漏风险。
修复方案
方案一:替换Handler类型,手动管理引用计数
将SimpleChannelInboundHandler换成ChannelInboundHandlerAdapter,避免自动释放响应对象,同时在使用完响应后手动释放:
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { if (msg instanceof FullHttpResponse) { FullHttpResponse response = (FullHttpResponse) msg; System.out.println(response); future.complete(response); System.out.println("response content: " + response.content().toString(CharsetUtil.UTF_8)); ctx.close(); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { future.completeExceptionally(cause); System.out.println("exceptionCaught"); ctx.close(); } });
然后在回调中使用完响应后手动释放:
responseFuture.thenAccept(response -> { System.out.println(response); ByteBuf content1 = response.content(); String responseBody = content1.toString(CharsetUtil.UTF_8); // 使用完毕后手动释放 ReferenceCountUtil.release(response); }).exceptionally(ex -> { ex.printStackTrace(); System.err.println("Request failed: " + ex.getMessage()); return null; });
方案二:保留SimpleChannelInboundHandler,增加引用计数
在channelRead0中调用response.retain(),提升引用计数,避免Handler自动释放后对象失效:
@Override protected void channelRead0(ChannelHandlerContext ctx, FullHttpResponse response) { System.out.println(response); // 增加引用计数,防止后续访问时被释放 response.retain(); future.complete(response); System.out.println("response content: " + response.content().toString(CharsetUtil.UTF_8)); ctx.close(); }
方案三:调整客户端同步逻辑
移除channel.closeFuture().sync(),避免阻塞等待通道关闭,让响应处理流程异步执行:
// 移除这行阻塞代码 // channel.closeFuture().sync();
修复请求ByteBuf的内存泄漏
在main方法中写入请求体后,释放临时ByteBuf:
request.content().writeBytes(content); // 释放临时ByteBuf content.release();
内容的提问来源于stack exchange,提问作者cobalt17

