You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Netty同步客户端抛出IllegalReferenceCountException问题排查求助

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 02:06:00