• 内存池泄露


     

    为了提升消息接收和发送性能,Netty针对 ByteBuf 的申请和释放采用池化技术,通过PooledByteBufAllocator可以创建基于内存池分配的ByteBuf对象,这样就避免了每次消息读写都申请和释放ByteBuf。由于ByteBuf涉及byte[]数组的创建和销毁,对于性能要求苛刻的系统而言,重用ByteBuf带来的性能收益是非常可观的。
    内存池是一把双刃剑,如果使用不当,很容易带来内存泄漏和内存非法引用等问题,另外,除了内存池,Netty同时也支持非池化的ByteBuf,多种类型的ByteBuf功能存在些差异,使用不当很容易带来各种问题。

    问题代码如图:

    这里很容易以为是响应消息ByteBuf未释放引起的,其实不是,因为ctx.writeAndFlush(respMsg)方法中netty会分两种情况自动释放:
    1.如果是堆内存(PooledHeapByteBuf),则将HeapByteBuffer转换成DirectByteBuffer,并释放PooledHeapByteBuf到内存池,如果消息完整地被写到SocketChannel中,则释放DirectByteBuffer。
    2.如果是DirectByteBuffer,则不需要转换,在消息发送完成后,由ChannelOutboundBuffer的remove()负责释放。
    而这里即使继承自ChannelInbundHandlerAdapter,请求消息reqMsg未被释放,这是因为实现的是channelRead,而不是channelRead0。

    【ByteBuf内存释放误区】

    分为4中情况:
    1. 基于内 存池的请求ByteBuf
    这类 ByteBuf主要包括PooledDirectByteBuf和PooledHeapByteBuf,它由NettyNioEventLoop线程在处理Channel的读操作时分配,需要在业务ChannelInboundHandler处理完请求消息之后释放(通常在解码之后),它的释放有两种策略。
    策略1 业务ChannelIlnboundHandler继承自SimpleChannelInboundHandler,实现它的抽象方法channelReadO(ChannelHandlerContext ctx, I msg),ByteBuf的释放业务不用关心,由SimpleChannelInboundHandler负责释放。
    策略2 在业务ChannelInboundHandler中调用ctx.fireChannelRead(msg)方法,让请求 消息继续向后执行,直到调用DefaultChannelPipeline的内部类TailContext,由它来负责释放请求消息,代码如下( TailContext):
    2.基于非内存池的请求ByteBuf
    如果业务使用非内存池模式覆盖Netty默认的内存池模式创建请求ByteBuf,例如通过如下代码修改内存申请策略为Unpooled:也需要按照内存池的方式释放内存。
    3.基于内存池的响应ByteBuf
    根据之前的分析, 只要调用了writeAndFlush或者flush方法,在消息发送完成后都会由Netty框架进行内存释放,业务不需要主动释放内存
    4.基于非内存池的响应ByteBuf
    无论是基于内存池还是非内存池分配的 ByteBuf,如果是堆内存,则将堆内存转换成堆外内存,然后释放 HeapByteBuffer,待消息发送完成,再释放转换后的 DirectByteBuf;
    如果是 DirectByteBuffer,则不需要转换,待消息发送完成之后释放。因此对于需要发送的响应ByteBuf,由业务创建,但是不需要由业务来释放。

  • 相关阅读:
    当EAI遇见RPA:破解接口难题,助力数据打通
    【机器学习】21天挑战赛学习笔记(二)
    uniapp 内容展开组件
    创建对象在堆区如何分配内存
    小程序上新(2022.10.13~11.14)
    澳鹏Appen重磅发布《2022人工智能与机器学习全景报告》
    21天学习挑战赛——Python操作XML文件
    [附源码]Python计算机毕业设计高校教材网上征订系统
    kafka的安装和基本操作
    mmdetection ValueError: need at least one array to concatenate解决方案
  • 原文地址:https://blog.csdn.net/qq_34448345/article/details/127439852