1. in.readBytes 导致堆外内存泄漏
使用netty 中偶现 LEAK: ByteBuf.release() was not called before it's garbage-collected. 在几次检查自己的代码,发现ByteBuf都有释放。就是找不到问题,翻阅了一下netty的官方文档,有两种方式可以 打印出泄漏的详细信息
- ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.ADVANCED);
- -Dio.netty.leakDetectionLevel=advanced
打印日志如下:
io.netty.buffer.AdvancedLeakAwareByteBuf.writeBytes(AdvancedLeakAwareByteBuf.java:600)
io.netty.buffer.AbstractByteBuf.readBytes(AbstractByteBuf.java:829)
com.XXX.XXX.XXX.network.codec.XXXTcpDecoder.decode(XXXTcpDecoder.java:55)
io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:489)
io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:428)
io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:265)
io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1334)
io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:926)
io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:134)
io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:644)
io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:579)
io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:496)
io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:458)
io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:138)
java.lang.Thread.run(Thread.java:748)
Created at:
io.netty.util.ResourceLeakDetector.track(ResourceLeakDetector.java:237)
io.netty.buffer.PooledByteBufAllocator.newDirectBuffer(PooledByteBufAllocator.java:331)
io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:181)
io.netty.buffer.AbstractByteBufAllocator.buffer(AbstractByteBufAllocator.java:117)
io.netty.buffer.AbstractByteBuf.readBytes(AbstractByteBuf.java:828)
com.XXX.XXX.XXX.network.codec.XXXTcpDecoder.decode(XXXTcpDecoder.java:55)
io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:489)
io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:428)
io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:265)
io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1334)
io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:926)
io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:134)
io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:644)
io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:579)
io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:496)
io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:458)
io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:138)
java.lang.Thread.run(Thread.java:748)
com.XXX.XXX.XXX.network.codec.XXXTcpDecoder.decode(XXXTcpDecoder.java:55
来看一下这一行明确提示泄漏的地方,然后看了一下对应的代码一脸懵逼
image.png
in.readBytes 需要释放bytebuf ???? 这他妈什么操作 吓得我赶紧看了一下源代码。。 注意 我特么也很sb 仔细看入参 一个是 long 一个是 byte[] 导致我浪费了很多时间
@Override
public ByteBuf readBytes(byte[] dst) {
readBytes(dst, 0, dst.length);
return this;
}
没毛病啊,非常奇怪。为啥!!!!! 然后就是各种google
加了 ReferenceCountUtil.release(in);
in.readByte();
int dataLength = remaining - 17;
byte[] dataByte = new byte[dataLength];
in.readBytes(dataByte);
String data = new String(dataByte);
in.readBytes(8);
。。。。。
ReferenceCountUtil.release(in);
不但没用,链接都报错了!!!!!
接着开始怀疑这个是不是netty自身的bug。。。
换了版本还是有问题!!!!! 受不了!!!!!
然后继续思考人生。无意再次点击看源码。。突然有一句mmp 不知当讲不当讲!!!
@Override
public ByteBuf readBytes(int length) {
checkReadableBytes(length);
if (length == 0) {
return Unpooled.EMPTY_BUFFER;
}
ByteBuf buf = alloc().buffer(length, maxCapacity);
buf.writeBytes(this, readerIndex, length);
readerIndex += length;
return buf;
}
.... readBytes方法返回新的byteBuf ???
image.png
那特么我刚开始看是哪个源码??? 然后把整个 AbstractByteBuf 看了一遍
内心崩溃 readBytes 方法有很多,每个入参不同,但是!!!!!!
readBytes(int length) 这个是返回一个新的bytebuf 所以需要释放掉!!!
弄了半天,还是自己sb。从错误中已经明确提示哪一行没有释放,但是结果还是饶了这么多弯。。。。。。
网友评论