Netty 中的 ByteBuffers 导致内存泄漏
Memory leak due to ByteBuffers in Netty
我创建了一个小型 Netty 服务器来计算 BigInteger 的阶乘并发送结果。代码如下
Factorial.java
public class Factorial {
private int port;
public Factorial(int port) {
this.port = port;
}
public void run(int threadcount) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup(threadcount);
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new FactorialHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture f = b.bind(port).sync();
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port = 15000;
new Factorial(port).run(Integer.parseInt(args[0]));
}
}
FactorialHandler.java
public class FactorialHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
BigInteger result = BigInteger.ONE;
String resultString;
for (int i=2000; i>0; i--)
result = result.multiply(BigInteger.valueOf(i));
resultString = result.toString().substring(0, 3)+"\n";
ByteBuf buf = Unpooled.copiedBuffer(resultString.getBytes());
ctx.write(buf);
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
当我运行这个我得到了以下错误
Jun 08, 2018 5:28:09 PM io.netty.util.ResourceLeakDetector reportTracedLeak
SEVERE: LEAK: ByteBuf.release() was not called before it's garbage-collected. See http://netty.io/wiki/reference-counted-objects.html for more information.
Recent access records:
正如给定的link中所解释的,我在ctx.flush()
之后的channelRead方法中调用buf.release()
释放了ByteBuffer。
但是当我这样做时,服务器开始抛出以下异常
io.netty.util.IllegalReferenceCountException: refCnt: 0, increment: 1
有人可以告诉我如何解决这个问题吗?
因为你没有调用msg.release()
(msg是ByteBuf的一个实例)
问题不在于出站 ByteBuf。 Outbound ByteBufs 总是为你处理(参见 OutboundMessages). The problem is the inbound ByteBuf. I'm looking at you, FactorialHandler. It extends ChannelInboundHandlerAdapter。注意 JavaDoc 中的这一点:
Be aware that messages are not released after the
channelRead(ChannelHandlerContext, Object) method returns
automatically. If you are looking for a ChannelInboundHandler
implementation that releases the received messages automatically,
please see SimpleChannelInboundHandler.
您的处理程序具有如下签名:
public void channelRead(ChannelHandlerContext ctx, Object msg)
那个 msg,(顺便说一句,你不使用它)实际上是一个 ByteBuf,这正是上面的 JavaDoc 注释警告你的内容。 (在没有任何其他 ChannelHandler 的情况下,消息将始终是 ByteBuf 的实例。)
所以你的选择是:
- 使用 SimpleChannelInboundHandler 将为您清理该引用。
- 在处理程序结束时,使用 ReferenceCountUtil.release(java.lang.Object msg) 释放入站 ByteBuf。
我创建了一个小型 Netty 服务器来计算 BigInteger 的阶乘并发送结果。代码如下
Factorial.java
public class Factorial {
private int port;
public Factorial(int port) {
this.port = port;
}
public void run(int threadcount) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup(threadcount);
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new FactorialHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture f = b.bind(port).sync();
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port = 15000;
new Factorial(port).run(Integer.parseInt(args[0]));
}
}
FactorialHandler.java
public class FactorialHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
BigInteger result = BigInteger.ONE;
String resultString;
for (int i=2000; i>0; i--)
result = result.multiply(BigInteger.valueOf(i));
resultString = result.toString().substring(0, 3)+"\n";
ByteBuf buf = Unpooled.copiedBuffer(resultString.getBytes());
ctx.write(buf);
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
当我运行这个我得到了以下错误
Jun 08, 2018 5:28:09 PM io.netty.util.ResourceLeakDetector reportTracedLeak
SEVERE: LEAK: ByteBuf.release() was not called before it's garbage-collected. See http://netty.io/wiki/reference-counted-objects.html for more information.
Recent access records:
正如给定的link中所解释的,我在ctx.flush()
之后的channelRead方法中调用buf.release()
释放了ByteBuffer。
但是当我这样做时,服务器开始抛出以下异常
io.netty.util.IllegalReferenceCountException: refCnt: 0, increment: 1
有人可以告诉我如何解决这个问题吗?
因为你没有调用msg.release()
(msg是ByteBuf的一个实例)
问题不在于出站 ByteBuf。 Outbound ByteBufs 总是为你处理(参见 OutboundMessages). The problem is the inbound ByteBuf. I'm looking at you, FactorialHandler. It extends ChannelInboundHandlerAdapter。注意 JavaDoc 中的这一点:
Be aware that messages are not released after the channelRead(ChannelHandlerContext, Object) method returns automatically. If you are looking for a ChannelInboundHandler implementation that releases the received messages automatically, please see SimpleChannelInboundHandler.
您的处理程序具有如下签名:
public void channelRead(ChannelHandlerContext ctx, Object msg)
那个 msg,(顺便说一句,你不使用它)实际上是一个 ByteBuf,这正是上面的 JavaDoc 注释警告你的内容。 (在没有任何其他 ChannelHandler 的情况下,消息将始终是 ByteBuf 的实例。)
所以你的选择是:
- 使用 SimpleChannelInboundHandler 将为您清理该引用。
- 在处理程序结束时,使用 ReferenceCountUtil.release(java.lang.Object msg) 释放入站 ByteBuf。