Netty 处理程序管道的典型引用计数

Netty 处理程序管道的典型引用计数,netty,Netty,试图升级到Netty 4.1.17.Final,我面临以下管道的ref计数问题 第一个处理程序正在对来自ByteBuf的原始输入进行帧处理:等待至少有最小数量的可读字节来使用一个帧,将可以从该原始输入读取的所有帧放入output in/out List参数中。帧是使用readSlice派生的缓冲区。 第二个处理程序将每个帧解析为特定于应用程序的类not ref counted。 据我所知,只有第二个处理程序应该调用release 不幸的是,在某些情况下,当缓冲区在第一次尝试读取时被回收时,它会引

试图升级到Netty 4.1.17.Final,我面临以下管道的ref计数问题

第一个处理程序正在对来自ByteBuf的原始输入进行帧处理:等待至少有最小数量的可读字节来使用一个帧,将可以从该原始输入读取的所有帧放入output in/out List参数中。帧是使用readSlice派生的缓冲区。 第二个处理程序将每个帧解析为特定于应用程序的类not ref counted。 据我所知,只有第二个处理程序应该调用release

不幸的是,在某些情况下,当缓冲区在第一次尝试读取时被回收时,它会引发非法引用计数异常:refCnt:0

我不清楚在这样的处理程序管道/链中,第一个应该保留缓冲区,还是不使用派生缓冲区

编辑1:与我从文档中了解到的相反,readRetainedSlice的行为与readSlice并不完全相同……在这种情况下,请保留,因为readRetainedSlice返回的缓冲区的refCnt与父缓冲区的refCnt不同

编辑2:使用leakDetection筛选泄漏检测详细信息。acquireAndReleaseOnly=false,问题似乎在出站端

23:20:55.586 ERROR [s.n.u.ResourceLeakDetector] :: 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: 
#1:
    io.netty.util.ReferenceCountUtil.release(ReferenceCountUtil.java:88)
    io.netty.util.ReferenceCountUtil.safeRelease(ReferenceCountUtil.java:113)
    io.netty.channel.ChannelOutboundBuffer.remove(ChannelOutboundBuffer.java:256)
    io.netty.channel.embedded.EmbeddedChannel.doWrite(EmbeddedChannel.java:724)
    io.netty.channel.AbstractChannel$AbstractUnsafe.flush0(AbstractChannel.java:934)
    io.netty.channel.AbstractChannel$AbstractUnsafe.flush(AbstractChannel.java:901)
    io.netty.channel.embedded.EmbeddedChannel$EmbeddedUnsafe$1.flush(EmbeddedChannel.java:820)
    io.netty.channel.DefaultChannelPipeline$HeadContext.flush(DefaultChannelPipeline.java:1321)
        ...
    io.netty.channel.AbstractChannel.writeAndFlush(AbstractChannel.java:300)
    ChannelFactorySpec$$anonfun$3$$anonfun$apply$4$$anonfun$apply$6.apply(ChannelFactorySpec.scala:92)
    ChannelFactorySpec$$anonfun$3$$anonfun$apply$4$$anonfun$apply$6.apply(ChannelFactorySpec.scala:89)
    NettyEmbedder$.withChannel(NettyEmbedder.scala:95)
    ChannelFactorySpec$$anonfun$3$$anonfun$apply$4.apply(ChannelFactorySpec.scala:89)
    ChannelFactorySpec$$anonfun$3$$anonfun$apply$4.apply(ChannelFactorySpec.scala:50)
        ...
#2:
    io.netty.buffer.AdvancedLeakAwareByteBuf.retain(AdvancedLeakAwareByteBuf.java:36)
    io.netty.util.ReferenceCountUtil.retain(ReferenceCountUtil.java:40)
    io.netty.channel.embedded.EmbeddedChannel.doWrite(EmbeddedChannel.java:722)
    io.netty.channel.AbstractChannel$AbstractUnsafe.flush0(AbstractChannel.java:934)
    io.netty.channel.AbstractChannel$AbstractUnsafe.flush(AbstractChannel.java:901)
    io.netty.channel.embedded.EmbeddedChannel$EmbeddedUnsafe$1.flush(EmbeddedChannel.java:820)
    io.netty.channel.DefaultChannelPipeline$HeadContext.flush(DefaultChannelPipeline.java:1321)
        ...
    io.netty.channel.DefaultChannelPipeline.writeAndFlush(DefaultChannelPipeline.java:1041)
    io.netty.channel.AbstractChannel.writeAndFlush(AbstractChannel.java:300)
    ChannelFactorySpec$$anonfun$3$$anonfun$apply$4$$anonfun$apply$6.apply(ChannelFactorySpec.scala:92)
    ChannelFactorySpec$$anonfun$3$$anonfun$apply$4$$anonfun$apply$6.apply(ChannelFactorySpec.scala:89)
    NettyEmbedder$.withChannel(NettyEmbedder.scala:95)
    ChannelFactorySpec$$anonfun$3$$anonfun$apply$4.apply(ChannelFactorySpec.scala:89)
    ChannelFactorySpec$$anonfun$3$$anonfun$apply$4.apply(ChannelFactorySpec.scala:50)
#3:
    Hint: 'DefaultChannelPipeline$HeadContext#0' will handle the message from this point.
    io.netty.channel.DefaultChannelPipeline.touch(DefaultChannelPipeline.java:116)
    io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:810)
    io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:723)
    io.netty.channel.ChannelOutboundHandlerAdapter.write(ChannelOutboundHandlerAdapter.java:104)
    NettyEmbedder$$anon$1.write(NettyEmbedder.scala:88)
        ...
    io.netty.handler.timeout.IdleStateHandler.write(IdleStateHandler.java:304)
    reactivemongo.core.protocol.MongoHandler.write(MongoHandler.scala:91)
        ...
Created at:
    Hint: 'NettyEmbedder$$anon$1#0' will handle the message from this point.
    io.netty.channel.DefaultChannelPipeline.touch(DefaultChannelPipeline.java:116)
    io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:810)
    io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:723)
    io.netty.handler.codec.MessageToByteEncoder.write(MessageToByteEncoder.java:113)
    io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:738)
    io.netty.channel.AbstractChannelHandlerContext.invokeWrite(AbstractChannelHandlerContext.java:730)
    io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:816)
    io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:723)
    io.netty.handler.timeout.IdleStateHandler.write(IdleStateHandler.java:304)
    reactivemongo.core.protocol.MongoHandler.write(MongoHandler.scala:91)
        ...
    io.netty.channel.AbstractChannel.writeAndFlush(AbstractChannel.java:300)
    ChannelFactorySpec$$anonfun$3$$anonfun$apply$4$$anonfun$apply$6.apply(ChannelFactorySpec.scala:92)
    ChannelFactorySpec$$anonfun$3$$anonfun$apply$4$$anonfun$apply$6.apply(ChannelFactorySpec.scala:89)
    NettyEmbedder$.withChannel(NettyEmbedder.scala:95)
        ...

因此,我查看了您的代码,发现存在缓冲区泄漏:

这将调用frame.readBytesreadableBytes,它将返回一个新的已分配ByteBuf,您只需将其放在地板上,而不调用release。在这里调用release应该可以修复这个漏洞的同时,我建议只调用frame.skipbytes readablebytes,它也会丢弃它,但是没有分配和内存拷贝

希望这能解决它


请注意,我的Scala技能有点生疏,所以可能我遗漏了一些东西:

如果你需要在应用程序POJO中使用Netty ByteBuf,你需要为你的POJO实现一个发布方法。如上所述,在第二个处理程序之后没有ref counted/able数据,为什么问题是关于管道的请显示你的第一个处理程序。。。它扩展/实现了什么类型?这是一个ByteToMessage,它将解码到下一个处理程序w/readSlice…retain:@NormanMaurer我想知道的是,某些情况/误用是否会导致这样的问题?目前,泄漏检测器正在抱怨使用此类通道的测试,如果且仅当管道配置了出站处理程序,并且如果且仅当此测试与另一个测试一起执行时,不使用任何嵌入式通道感谢您的时间@norman maurer。在中,跟踪是由测试使用EmbeddedChannel重新生成的,其中没有调用readBytes的入站处理程序,因此我认为这没有关系。