1、为什么netty服务端启动后又无异常地自动退出?
由于代码1处执行完后直接进入2、3,那么netty服务端就会关闭退出。
解决一、直接在代码1后面处加上同步阻塞sync,那么只有服务端正常关闭channel时才会执行下面的语句
解决二、把代码2和3移到operationComplete里面,那么也只有channel关闭时才会让netty的两个线程组关闭
2、netty客户端连接池资源OOM
生产环境用netty作为客户端,为了提高性能,客户端与服务端创建多条链路,同时客户端创建一个TCP连接池。结果业务高峰期OOM
从异常日志和线程资源占用来看,导致内存泄漏的原因是应用创建了大量的EventLoopGroup线程池。这就是一个TCP连接对应一个NIO线程的模式。错误之在就是采用BIO模式来调用NIO通信框架,不仅没优化效果,还发生了OOM。
正确操作是
注意:Bootstrap自身不是线程安全的,但。connect方法它会创建一个新的NioSocketChannel,并从初始构造的EventLoopGroup中选择一个NioEventLoop线程执行真正的Channel连接操作,与执行Boostrap的线程无关。在同一个Boostrap创建多个客户端连接,EventLoopGroup是共享的,这些连接共用同一个NIO线程组EventLoopGroup,当某个链路发生异常或关闭时,只需要关闭并释放Channel本身即可,不能同时销毁NioEventLoop和所在线程组EventLoopGroup,
3、netty居然会产生内存池泄漏问题
在调用ctx.writeAndFlush方法时,当消息发送完成,Netty会主动帮助应用释放内存,内存释放场景如下
(1)如果是堆内存(PooledHeapByteBuf),则将HeapByteBuffer转换成DirectByteBuffer,并释放PooledHeapByteBuf到内存池。
(2)如果是DirectByteBuffer,则不需要转换,在消息发送完成后,由ChannelOutboundBuffer的remove方法负责释放
为了在实际项目中更好地管理ByteBuf,下面分4种场景说明
(1)基于内存池的请求ByteBuf,这类主要包括PooledDirectByteBuf和PooledHeapByteBuf,它由NioEventLoop线程在处理Channel读操作时分配,需要在业务ChannelInboundHandler处理完请求消息后释放(通常在解码之后),它的释放策略如下:
ChannelInboundHandlerSimpleChannelInboundHandler,实现它的抽象方法channelRead0,ByteBuf的释放业务不用关心,由SimpleChannelInboundHandler负责释放
在业务ChannelInboundHandler中调用ctx.fireChannelRead(msg),让请求继续向后执行,直至调用DefaultChannelPipeline的内部类TailContext,由它负责释放请求信息
直接调用在channelRead方法里调用ReferenceCountUtil.release(reqMsg)
(2) 基于非内存池的请求ByteBuf,它也是需要按照内存池的方式释放内存
(3)基于内存池的响应ByteBuf,根据之前的分析,只要调用了writeAndFlush或flush方法,在消息发送完成后都会由Netty框架进行内存释放,业务不需要主动释放内存
(4)基于非内存池的响应ByteBuf,业务不需要主动释放内存
当然非内存池也不一定要手动释放,但最好手动释放。Netty里有4种主力的ByteBuf,其中UnpooledHeapByteBuf底下的byte[]能够依赖JVM GC自然回收;而UnpooledDirectByteBuf底下是DirectByteBuffer,是Java堆外内存,除了等JVM GC,最好也能主动进行回收,否则导致内存不足也产生内存泄露的假象;而PooledHeapByteBuf和PooledDirectByteBuf,则必须要主动将用完的byte[]/ByteBuffer放回池里,否则内存就要爆掉。
对于内存池泄露可以的监控可以配置启动参数
不同参数信息如下:
DISABLED
完全关闭内存泄露检测,并不建议SIMPLE
以1%的抽样率检测是否泄露,默认级别ADVANCED
抽样率同SIMPLE
,但显示详细的泄露报告PARANOID
抽样率为100%,显示报告信息同ADVANCED
最后,悄悄告诉你,网上的你些netty入门demo大都存在内存池泄露问题,只不过数据量传输少,可能运行大半年才会出现LEAK,就连《netty权威指南》入门demo也存在这个问题,也许就只是个入门demo,所以不弄得太复杂。什么你不信,你可以在入门demo的TimeClientHandler或TimeServerHandler加上下面这坨代码。
ByteBuf firstMessage=null; for (int j=0;j & lt;Integer.MAX_VALUE;j + +) { firstMessage=Unpooled.buffer (1024); for (int i=0;我& lt;firstMessage.capacity ();我+ +){ firstMessage.writeByte((字节)我); } ctx.writeAndFlush (firstMessage); }网状的填坑——论填坑,我是专业的